1

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?

Spook
  • 25,318
  • 18
  • 90
  • 167

1 Answers1

2

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)));
    }
David Tchepak
  • 9,826
  • 2
  • 56
  • 68
  • How do you "...manually asserting on the inner exception..."? – h bob Aug 10 '16 at 20:20
  • 1
    @hbob: I've added examples of both approaches. Please let me know if you'd like any more information. – David Tchepak Aug 11 '16 at 06:46
  • There's another way. If you have `Dog : AAnimal` and want to test the abstract `AAnimal`, you create abstract `AAnimalTests` with appropriate tests. Then create a `DogTests : AAnimalTests` class. When its tests are run, they operate on a concrete implementation (`Dog`), so you don't need a fake. Gets a little messy when there are multiple implementations (`Cat`, `Horse`, `Bird`, etc.) because the "same" tests are run for every implementation, but maybe that's not a bad thing. I don't know if this is "best practice", but I've been doing it for a while. What do you think? – h bob Aug 11 '16 at 11:37