Spook
Spook

Reputation: 25927

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?

Upvotes: 1

Views: 727

Answers (1)

David Tchepak
David Tchepak

Reputation: 10484

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)));
    }

Upvotes: 2

Related Questions