Reputation: 25927
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
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