2011-03-02 33 views
6

我只是在玩.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,但我会预期代码合同停止进入switch语句的“9”的值。这是预期的行为?

回答

1

如果您的函数的规范使用英文表示,它接受hours的任何值,并在hours不在0..8范围内时引发异常,则其合同(以代码合同语言表示)不是它需要hours在0和8之间。正确的翻译是函数不需要任何东西,并且它确保如果hours在错误的范围内,则引发异常,并且确保如果hours处于正确的范围,正确的计算完成。

我希望有一种方法可以在代码合同中表达这些东西,但我不熟悉这种契约语言,只有另一种。不过,哲学是一样的:如果你想把支票作为生产版本的一部分,那么支票的条件不是先决条件。另一方面,合同可能(应该)表示支票已经发出,并且每个案件都得到了适当的处理。

+0

感谢帕斯卡尔,它确实清除了一些东西。我认为代码合同可以取代所有参数检查,但如果我希望他们在生产版本中,他们仍然需要保留。 – fluent 2011-03-03 09:08:34

+0

更新:下面的Porges指出我正确的方向,编辑项目设置以启用生产版本中的合同。 – fluent 2011-03-03 09:18:30

-1

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

你应该定义可以通过的规则。

,所以我认为这会为你工作...

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

谢谢,你在我的文章中发现了一个有点误导的错误。原来的if ... then .. throw是错误的,现在我已经纠正了。但问题依然存在。 – fluent 2011-03-02 10:27:45

+4

-1:这没有任何意义。你基本上认为你需要同时小于零和大于8。 – Virtlink 2012-03-22 12:54:11

0

好吧,我现在有代码启用抛出预期的错误“执行运行时检查合同”,在Visual Studio中的项目属性中选择。

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

我明显地误解了代码合约应该如何使用,所以如果有人可以指向我的最佳实践文章的方向,我会很感激。

在MSDN页面状态:

“合同类的大多数方法是有条件编译;也就是说,编译器生成这些方法的调用,只有当你定义一个特殊的符号,合同FULL,使用中的#define指令CONTRACTS FULL可以让你在不使用#ifdef指令的情况下在你的代码中编写契约;你可以产生不同的版本,有些版本有契约,有些则没有。“

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

+0

对于答案中的所有问题,我会说“是”,我会扩展一点点,但StackOverflow注释有点限制,所以我写了另一个答案。 – 2011-03-02 11:42:09

+1

rggardner:项目属性中有很多选项:)您可以为发布构建启用运行时实施,或者根据需要禁用它。这取决于你想要对合同做什么。你也可以只在面向公众的方法,如 – porges 2011-03-03 00:17:41

+0

感谢Porges,非常有用的知道。我现在需要做的是将正确的设置工作到我的构建文件中。 – fluent 2011-03-03 09:17:13