Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing code contracts

I'm just having a play with the code contracts in .Net 4.0 and must be missing something obvious as they are not behaving as I would expect.

I have always used a simple if... then.. throw statement to perform any validation at the start of a function.

if (hours < 0 || hours > 8)
    throw new ArgumentOutOfRangeException("hours", "Hours must be between 0 and 8");

I have simply replaced this with

Contract.Requires<ArgumentOutOfRangeException>(hours >= 0 && hours <= 8, "Hours must be between 0 and 8");

but it never seems to throw an issue on my unit tests.

    public static DurationUnit HoursAsDuration(int hours)
    {
        Contract.Requires<ArgumentOutOfRangeException>(hours >= 0 && hours <= 8, "Hours must be between 0 and 8");

        switch (hours)
        {
            case 1:
            case 2:
                return DurationUnit.Quarter;
            case 3:
            case 4:
                return DurationUnit.Half;
            case 5:
            case 6:
                return DurationUnit.ThreeQuarter;
            case 7:
            case 8:
                return DurationUnit.Full;
            default:
                return DurationUnit.None;
        }
    }

    [Test]
    public void CanConvertToDuration()
    {
        Assert.AreEqual(DurationUnit.None, DateTimeUtility.HoursAsDuration(0));
        Assert.AreEqual(DurationUnit.Quarter, DateTimeUtility.HoursAsDuration(1));
        Assert.AreEqual(DurationUnit.Quarter, DateTimeUtility.HoursAsDuration(2));
        Assert.AreEqual(DurationUnit.Half, DateTimeUtility.HoursAsDuration(3));
        Assert.AreEqual(DurationUnit.Half, DateTimeUtility.HoursAsDuration(4));
        Assert.AreEqual(DurationUnit.ThreeQuarter, DateTimeUtility.HoursAsDuration(5));
        Assert.AreEqual(DurationUnit.ThreeQuarter, DateTimeUtility.HoursAsDuration(6));
        Assert.AreEqual(DurationUnit.Full, DateTimeUtility.HoursAsDuration(7));
        Assert.AreEqual(DurationUnit.Full, DateTimeUtility.HoursAsDuration(8));

        //Would expect this to cause an issue
        Assert.AreEqual(DurationUnit.None, DateTimeUtility.HoursAsDuration(9));
    }

The test returns true but I would have expected the code contract to stop the value of "9" getting into the switch statement. Is this the expected behaviour?

like image 799
fluent Avatar asked Mar 02 '11 10:03

fluent


1 Answers

If your function's specification, expressed in English, is that it accepts any value for hours and throws an exception when hours is not in the range 0..8, then its contract (expressed in Code Contracts language) is not that it requires hours to be between 0 and 8. The proper translation is that the function doesn't require anything, and it ensures that if hours was in the wrong range, an exception has been raised, and it ensures that if hours was in the correct range, the proper computation was done.

I would expect there is a way to express these things in Code Contracts, but I am not familiar with this contract language, only with another one. The philosophy is the same though: if you want the check to be part of the production build, then the condition of the check is not a pre-condition. On the other hand, the contract may (should) express that the check is made and that each case is handled appropriately.

like image 60
Pascal Cuoq Avatar answered Sep 23 '22 20:09

Pascal Cuoq