简体   繁体   中英

TargetInvocationException in NSubstitute

I want to write a test checking, whether my abstract classes constructor correctly handles invalid arguments. I wrote a test:

[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void MyClassCtorTest()
{
    var dummy = Substitute.For<MyClass>("invalid-parameter");
}

This test does not pass, because NSubstitute throws a TargetInvocationException instead of ArgumentException . The actual exception I seek for is actually an InnerException of that TargetInvocationException . I can write a helper method like:

internal static class Util {

    public static void UnpackException(Action a) {

        try {

            a();
        } catch (TargetInvocationException e) {

            throw e.InnerException;
        } catch (Exception) {

            throw new InvalidOperationException("Invalid exception was thrown!");
        }
    }
}

But I guess, that there rather should be some kind of general way of solving that problem. Is there one?

NSubstitute does not currently have a general way of solving this.

Some other workarounds include manually subclassing the abstract class to test the constructor, or manually asserting on the inner exception rather than using ExpectedException .

For example, say we have an abstract class that requires a non-negative integer:

public abstract class MyClass {
    protected MyClass(int i) {
        if (i < 0) {
            throw new ArgumentOutOfRangeException("i", "Must be >= 0");
        }
    }
    // ... other members ...
}

We can create a subclass in a test fixture to test the base class constructor:

[TestFixture]
public class SampleFixture {
    private class TestMyClass : MyClass {
        public TestMyClass(int i) : base(i) { }
        // ... stub/no-op implementations of any abstract members ...
    }

    [Test]
    [ExpectedException(typeof(ArgumentOutOfRangeException))]
    public void TestInvalidConstructorArgUsingSubclass()
    {
        new TestMyClass(-5);
    }
    // Aside: I think `Assert.Throws` is preferred over `ExpectedException` now.
    // See http://stackoverflow.com/a/15043731/906
}

Alternatively you can still use a mocking framework and assert on the inner exception. I think this is less preferable to the previous option as it is not obvious why we're digging in to the TargetInvocationException , but here's an example anyway:

    [Test]
    public void TestInvalidConstructorArg()
    {
        var ex = Assert.Throws<TargetInvocationException>(() => Substitute.For<MyClass>(-5));

        Assert.That(ex.InnerException, Is.TypeOf(typeof(ArgumentOutOfRangeException)));
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM