Saturday, December 8, 2012

Method pre-condition checking: IF, Assert and Contract.Requires

Recently I picked up on a different way of checking method pre- and post-conditions in c#: Code Contracts. In this post, I'll describe the three methods that I know of by providing an example and checking unit-test behavior:
  1. Good old 'if ()' checking with throwing exceptions
  2. Assertions, from the System.Diagnostics.Debug library
  3. Code contracts, form the System.Diagnostics.Contracts library, shipped with .NET framework 4
I've been playing around with it and I'm trying to make up my mind if I prefer any of the System.Diagnostics ways over the 'good-old' IF and exceptions.

Everyone uses division in order to demonstrate illegal argument handling, since division by 0 cannot be done, so why should I do it any differently? Let's look at a method:

        public static int Divide(int numerator, int denominator)
        {
            return numerator/denominator;
        }

Now, what we want to do is make sure that the denominator is larger than 0. Using the three mentioned methods, it would look something like this:


        // CASE 1: Throws exception in case pre-condition ( denominator > 0 ) is not met
        public static int DivideWithExceptionThrowing(int numerator, int denominator)
        {
            if (denominator == 0)
            {
                throw new DivideByZeroException("Denominator should be larger than zero");
            }
            return numerator / denominator;
        }

        // CASE 2: Fails assertion if pre-condition is not met
        public static int DivideWithAssert(int numerator, int denominator)
        {
            Assert.IsTrue(denominator > 0, "Denominator should be larger than zero");
            return numerator / denominator;
        }

        // CASE 3: Fails to meet contract requirements in case pre-condition is not met
        public static int DivideWithContract(int numerator, int denominator)
        {
            Contract.Requires(denominator > 0, "Denominator should not be larger than zero");
            return numerator / denominator;
        }
In case the last way of checking the pre-condition is new to you, check out this excellent guide. Although the namespaces are included in .NET framework 4.5, you do need to download the Visual Studio add-on for code contracts ( even if you are using VS2012 ) from here. After that you have to open solution properties and go to the code-contracts tab in order to set things up.
Also - I needed to set CONTRACTS_FULL compilation symbol in order for my ReSharper to stop fading out my Contract.Requires line, saying that it will be skipped ( as described here ).

Note that I can set the exception that needs to be thrown in case the pre-condition is not met, using Contract.Requires<ExceptionType>() syntax.

So now, how should we unit-test our three methods? I've set up three tests that supply a zero as denominator. Therefore - I'm checking for the exceptions using the ExpectedException attribute for all of these cases:

        [TestMethod]
        [ExpectedException(typeof(DivideByZeroException))]
        public void Divide_DividesByZero_ThrowsException()
        {
            const int numerator = 1;
            const int denominator = 0;
            var actual = MathematicalFunctions.DivideWithExceptionThrowing(numerator, denominator);
        }

        [TestMethod]
        [ExpectedException(typeof (AssertFailedException))]
        public void Divide_DividesByZero_FailsAssertion()
        {
            const int numerator = 1;
            const int denominator = 0;
            var actual = MathematicalFunctions.DivideWithAssert(numerator, denominator);
        }

        [TestMethod]
        [ExpectedException(typeof(DivideByZeroException))]
        public void Divide_DividesByZero_FailsToMeetContractRequirement()
        {
            const int numerator = 1;
            const int denominator = 0;
            var actual = MathematicalFunctions.DivideWithContract(numerator, denominator);
        }

Note here that if you have set up static checking ( see the code contracts tab in project properties ) - you will see that the contract checking will point out that things are going to cause problems. Be sure to do this on the calling assembly - that is the test project in my case:


When running these tests, one will get a popup saying that the contract requirements failed to be met:


By clicking ignore - the test will continue, the exception will be thrown and the test will pass. However, this is inconvenient and we can easily disable this by setting the "Runtime Checking" to "None" on the assemblies that are being called by the test.

Now - it's happy testing from this point:


Bottom line...

Obviously the contract checking is more advanced and the other two:

  1. The most important benefit is of course compile time feedback on the written code. The static contract checking. You can detect an error even before running a test or the application.
  2. It's less verbose than the IF-Exception way, because it takes up a third of the number of lines.
  3. It provides more control over the exception that will be thrown than Assertions. As far as I know, either an AssertionFailedException is thrown or not.
I cannot find any downsides at this point. Feel free to leave any feedback about aspects I might have overlooked.

No comments:

Post a Comment