2009/04/29

Presentation: Gallio, a .NET Testing Automation Platform

InfoQ made available this nice video recorded at QCon SF 2008, where my friend Jeff Brown talks about Gallio and MbUnit.

The mugs on the console can be bought on Gallio Store (Zazzle) ;)

2009/04/18

Asserting the inner exception

Asserting an expected exception is easy with MbUnit. Most of the time, the simple use of ExpectedExceptionAttribute (and similar) is sufficient:
[Test]
[ExpectedArgumentNullException]
public void DoSomething_with_null_argument_should_throw_exception()
{
var foo = new Foo();
foo.DoSomething(null);
}
This is a very handy solution. But it does not come without any inconvenient:
  1. Even if the test passes, you cannot be sure that the exception was thrown exactly where it was expected to be thrown. It is possible that another portion of the code has thrown an exception of the same type, before the expected failure point was reached. For example, a bug in the Foo constructor could have thrown the same exception; and it would make your test completely irrelevant because it would pass for a wrong reason.
  2. You cannot perform any additional assertion about the exception itself and its properties, like the message, or the inner exception.
Fortunately, MbUnit has a very useful Assert.Throws method, which solves all those limitations. It is very easy to use, in particular with the lambda expression syntax.
[Test]
public void DoSomething_with_null_argument_should_throw_exception()
{
var foo = new Foo();
Assert.Throws<ArgumentNullException>(() => foo.DoSomething(null));
}
Remark that ExpectedArgumentNullExceptionAttribute is no longer needed. Now, you are sure that the exception was thrown when passing a null reference to the DoSomething method, and not before, in an obscure part of the Foo constructor.

But wait! you can do even more! Unlike most of the other MbUnit assertion methods which just return void, Assert.Throws returns the instance of the exception which was caught. With that instance, you can make some more interesting assertions:

[Test]
public void DoSomething_with_null_argument_should_throw_exception()
{
var foo = new Foo();
var exception = Assert.Throws<ArgumentNullException>(() => foo.DoSomething(null));
Assert.AreEqual("Detected breach in space-time continuum.", exception.Message);
}
This is particularly useful for parameterized tests based on reflection. Indeed, reflective invocations encapsulate the exceptions into a TargetInvocationException instance, which makes the reason for the error a bit less discoverable:
public Foo2
{
public void SetInt32(int value) { // ... }
public void SetDouble(double value) { // ... }
public void SetInt16(short value) { // ... }
}
[Test]
[Row(typeof(int), 9999, "SetInt32")]
[Row(typeof(double), 9999.9, "SetDouble")]
[Row(typeof(short), 9999, "SetInt16")]
public void Set_too_large_value_should_throw_exception<T>(T tooLargeValue, string methodName)
{
var foo2 = new Foo2();
var method = typeof(Foo2).GetMethod(methodName);
var exception = Assert.Throws<TargetInvocationException>(() => method.Invoke(foo2, new object[] { tooLargeValue }));
Assert.IsInstanceOfType<ArgumentOutOfRangeException>(exception.InnerException);
}