If your are not already using CodeContracts I really recommend to start using them now. And if you are already using them, you will have noticed that their exceptions cannot easily be caught. This is due to a design decision from the design team as stated in their Code Contracts User Manual:
7.6 ContractException
The ContractException type is not a public type and is emitted as a nested private type into each assembly for which runtime contract checking is enabled. It is thus not possible to write catch handlers catching only ContractException. Contract exceptions can thus only be handled as part of a general exception backstop. The rationale for this design is that programs should not contain control logic that depends on contract failures, just like programs should not catch ArgumentNullException or similar validation exceptions.
However, in some cases, especially when writing unit tests, it is helpful to provide a means to at least test these exceptions. When using the Microsoft Unit Testing Framework you can certainly decorate your test method with a generic ExpectedException(typeof(Exception)
but this will not really tell you if a ContractException has really been thrown.
Sou to mitigate this shortcoming we can define our own attribute to decorate our test methods. This attribute is a standard attribute that derives from ExpectedExceptionBaseAttribute
and checks for the exception name:
using System; using System.Globalization; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace biz.dfch.CS.Appclusive.Core.Tests { public sealed class ExpectContractExceptionAttribute : ExpectedExceptionBaseAttribute { const string ContractExceptionName = "System.Diagnostics.Contracts.__ContractsRuntime+ContractException"; protected override void Verify(Exception exception) { if (exception.GetType().FullName != ContractExceptionName) { base.RethrowIfAssertException(exception); throw new Exception( string.Format( CultureInfo.InvariantCulture, "Test method {0}.{1} threw exception {2}, but contract exception was expected. Exception message: {3}", base.TestContext.FullyQualifiedTestClassName, base.TestContext.TestName, exception.GetType().FullName, exception.Message ) ); } } } }
To utilise this attribute you only have to write your test method like this:
[TestMethod] [ExpectContractException] public void DividingByZeroThrowsContractException() { double dividend = 4; double divisor = 0; var result = Divide(dividend, divisor); } private double Divide(double dividend, double divisor) { Contract.Requires(0 != divisor); return dividend / divisor; }
1 Comment »