Sunday, December 13, 2009

NUnit and Microsoft Contracts library

I’m using NUnit and Microsoft Code Contracts library to implement unit tests and code contracts (mostly preconditions).

Unfortunately Contracts library throws a generated (internal) exception class that can’t be used as Assert.Throws<ContractException>(…).

Contracts documentation suggests the following code for MSTest framework:

[TestClass]
public  class  Test
{
    [AssemblyInitialize]
    public  static  void   AssemblyInitialize (TestContext tc)
    {
        Contract. ContractFailed+= (sender, e) => {
            e.SetHandled();
            e.SetUnwind(); // cause code to  abort  after  event
            Assert.Fail(e.FailureKind.ToString() + ":" + e.DebugMessage);
        };
    }
}

I wrote the following code to mimic this functionality:

[TestFixtureSetUp]
public void SetUpFixture()
{
    Contract.ContractFailed +=
    (sender, e) =>
    {
        e.SetHandled();
        e.SetUnwind();
        Assert.Fail(string.Format("{0}:{1}", e.FailureKind.ToString(), e.Message));
    };
}

Well, it did not work, I couldn’t make the Assert.Throws structure work. I always got the ContractException exception. Finally I found a blog entry (in German) with a classic solution:

  1. use try/catch block
  2. check exception type name

So here is the working code:

try
{
    // Method under test
}
catch (Exception ex)
{
    Assert.True(ex.GetType().Name == "ContractException");
    return;
}
Assert.Fail("ContractException has not been thrown.");

2 comments:

  1. If you want your tests to exercise actual contract failures, you have to be extremely careful in how you decide if your test succeeds or fails. What you suggest above will also make the test succeed if the contract failure is not the one you expect, but a real failure in the test case.
    To handle the above scenario, you would want to customize the runtime contract behavior as described in sectin 7.7 of the documentation. This way, you can throw e.g., your own exception and in the catch handler of the test, compare the expected to fail condition string. This way, you make sure that the failure is the expected one.
    In general, testing for failing contracts is not recommended, just like you are not writing test cases that will make your "asserts" fail.

    ReplyDelete
  2. Thanks Manuel, you are right!
    The solution above is just a quick and dirty workaround.

    ReplyDelete