单元测试代码合同

时间:2011-03-02 10:16:45

标签: c#-4.0 nunit code-contracts

我只是在.Net 4.0中玩代码合约,并且必须缺少一些明显的东西,因为它们没有像我期望的那样表现。

我总是使用一个简单的if ... then .. throw语句在函数开头执行任何验证。

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

我只是将其替换为

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

但它似乎从未在我的单元测试中引发问题。

    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));
    }

测试返回true,但我原本期望代码合同停止“9”的值进入switch语句。这是预期的行为吗?

3 个答案:

答案 0 :(得分:1)

如果你的函数规范用英语表示,它接受hours的任何值,并在hours不在0..8范围内时引发异常,那么它的合同(用英文表示)代码契约语言)并不要求hours介于0和8之间。正确的翻译是函数不需要任何东西,它确保如果hours在错误的范围内,已经引发了异常,它确保如果hours在正确的范围内,则进行了正确的计算。

我希望有一种方法可以在代码合同中表达这些内容,但我不熟悉这种合同语言,只有另一种合同语言。但理念是相同的:如果您希望检查是生产构建的一部分,那么检查的条件不是先决条件。另一方面,合同可以(应该)表明支票已经完成并且每个案件都得到了适当的处理。

答案 1 :(得分:0)

好的,我现在通过启用visual studio中项目属性中的“执行运行时合同检查”选项来抛出预期错误。

这是否意味着当发布到生产时代码合同被有效忽略?

我很容易误解如何使用代码合同,所以如果有人能指出我的最佳实践文章的方向,我将不胜感激。

msdn页面说明:

“合同类中的大多数方法都是有条件编译的;也就是说,只有在使用#define指令定义特殊符号CONTRACTS FULL时,编译器才会发出对这些方法的调用.CONTRACTS FULL允许您在合同中写入合同不使用#ifdef指令的代码;你可以生成不同的构建,有些是契约,有些是没有。“

这是否意味着我仍然会更好地使用if .. then .. throw .. for public facing?参数验证检查确实有助于减少数据损坏,因为它会尽早标记故障点。

答案 2 :(得分:-1)

我认为你需要在你的小时参数上交换测试。

您应该定义将通过的规则。

所以我认为这对你有用......

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