views:

851

answers:

6

Hello,

I'm completely new at C# and NUnit.

In Boost.Test there is a family of BOOST_*_THROW macros. In Python's test module there is TestCase.assertRaises method.

As far as I understand it, in C# with NUnit (2.4.8) the only method of doing exception test is to use ExpectedExceptionAttribute.

Why should I prefer ExpectedExceptionAttribute over - let's say - Boost.Test's approach? What reasoning can stand behind this design decision? Why is that better in case of C# and NUnit?

Finally, if I decide to use ExpectedExceptionAttribute, how can I do some additional tests after exception was raised and catched? Let's say that I want to test requirement saying that object has to be valid after some setter raised System.IndexOutOfRangeException. How would you fix following code to compile and work as expected?

[Test]
public void TestSetterException()
{
    Sth.SomeClass obj = new SomeClass();

    // Following statement won't compile.
    Assert.Raises( "System.IndexOutOfRangeException",
                   obj.SetValueAt( -1, "foo" ) );

    Assert.IsTrue( obj.IsValid() );
}


Edit: Thanks for your answers. Today, I've found an It's the Tests blog entry where all three methods described by you are mentioned (and one more minor variation). It's shame that I couldn't find it before :-(.

+7  A: 

If you can use NUnit 2.5 there's some nice helpers there.

Assert.That( delegate { ... }, Throws.Exception<ArgumentException>())
Cristian Libardo
+1. I prefer using this style as there's less ceremony required to write the test, especially if you need to keep a reference for checking messages etc. You can also use var exception = Assert.Throws<MyExceptionType>(() => myInstance.DoSomethingInvalid());
Mark Simpson
+2  A: 

I've always adopted the following approach:

bool success = true;
try {
    obj.SetValueAt(-1, "foo");
} catch () {
    success = false;
}

assert.IsFalse(success);

...
David Arno
Personally I'd catch a specific exception, but that'll work ;-p
Marc Gravell
@Marc, valid point: it should catch the specific exception under test and let others through so that they appear as errors rather than test failures.
David Arno
+2  A: 

The MbUnit syntax is

Assert.Throws<IndexOutOfRangeException>(delegate {
    int[] nums = new int[] { 0, 1, 2 };
    nums[3] = 3;
});
Justice
+11  A: 

I'm surprised I haven't seen this pattern mentioned yet. David Arno's is very similar, but I prefer the simplicity of this:

try
{
    obj.SetValueAt(-1, "foo");
    Assert.Fail("Expected exception");
}
catch (IndexOutOfRangeException)
{
    // Expected
}
Assert.IsTrue(obj.IsValid());
Jon Skeet
Won't Assert.Fail("...") dump you out of the test with an AssertionFailedException?
Bill the Lizard
Yes. However, if the call to SetValueAt throws an exception (which we want to happen), control immediately moves to the catch-block and bypasses Assert.Fail, thus passing the test. If SetValueAt does not throw an exception, then behavior is not as excepted and the test should fail.
Justice
What Justice said :)
Jon Skeet
Snap, you're right.
Bill the Lizard
That is neater than my way of doing it. I'll use your approach in future :)
David Arno
+2  A: 

Your preferred syntax:

Assert.Raises( "System.IndexOutOfRangeException",
               obj.SetValueAt( -1, "foo" ) );

woiuldn't work with C# anyway - the obj.SetValueAt would be evaluated and the result passed to Assert.Raises. If SetValue throws an exception, then you'd never get into Assert.Raises.

You could write a helper method to do it:

void Raises<T>(Action action) where T:Exception {
   try {
      action();
      throw new ExpectedException(typeof(T));
   catch (Exception ex) {
      if (ex.GetType() != typeof(T)) {
         throw;
      }
   }
}

Which allows the similar syntax of:

Assert.Raises<System.IndexOutOfRangeException>(() => 
  obj.SetValueAt(-1, "foo")
;
Mark Brackett
+1  A: 

The best would be to use Visual T#: a free programming language (C# v2.0 compatible) for Microsoft.NET.

You then only have one line of code to test any assertion you want. Your sample would be:

test 
{
  Sth.SomeClass obj = new SomeClass();

  runtest obj.SetValueAt( -1, "foo" );

  assert thrown System.IndexOutOfRangeException;
  assert obj.IsValid();
}
Ludovic Dubois
While this post made me look up T# (and I do find it pretty interesting - recpect for your work!) - you should really mention that you're affiliated (or actually the author) of T# when you suggest that someone shall use it.You've done a good job of explaining in the Montreal presentationslides why the NUnit attribute approach is problematic (e.g. test succeeds even if initialization or verification code throws the exception to catch), why not also mention this here?
Lucero