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:
- use try/catch block
- 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.");
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.
ReplyDeleteTo 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.
Thanks Manuel, you are right!
ReplyDeleteThe solution above is just a quick and dirty workaround.