如何测试永远不会执行的代码?

时间:2010-08-20 21:57:22

标签: c# unit-testing testing code-coverage

如果已经验证存在无效数字(通过调用另一种方法),则仅调用以下方法。如何测试覆盖以下代码段中的throw - 行?我知道一种方法可能是将VerifyThereAreInvalidiDigits和此方法合并在一起。我正在寻找其他任何想法。

public int FirstInvalidDigitPosition {
    get {
        for (int index = 0; index < this.positions.Count; ++index) {
            if (!this.positions[index].Valid) return index;
        }
        throw new InvalidOperationException("Attempt to get invalid digit position whene there are no invalid digits.");
    }
}

我也不想编写一个单元测试来运行永远不会被执行的代码。

5 个答案:

答案 0 :(得分:22)

如果有问题的“throw”语句在任何可能的情况下都是无法访问,那么它应该已删除并替换为:

Debug.Fail("This should be unreachable; please find and fix the bug that caused this to be reached.");

如果代码可以访问,那么编写一个测试该场景的单元测试。公共可访问方法的错误报告方案是完全有效的方案。您必须正确处理所有输入,甚至输入错误。如果正确的做法是抛出一个异常,那么测试你是否抛出异常。

更新:根据评论,实际上不可能发现错误,因此代码无法访问。但是现在Debug.Fail也不可访问,并且它不能编译,因为编译器注意到返回值的方法具有可到达的终点。

第一个问题实际上不应该是一个问题;当然,代码覆盖率工具应该是可配置的,以忽略无法访问的仅调试代码。但是这两个问题都可以通过重写循环来解决:

public int FirstInvalidDigitPosition 
{ 
    get 
    { 
        int index = 0;
        while(true) 
        {
            Debug.Assert(index < this.positions.Length, "Attempt to get invalid digit position but there are no invalid digits!");
            if (!this.positions[index].Valid) return index; 
            index++;
        } 
    }
}

另一种方法是重新组织代码,以便您首先没有问题:

public int? FirstInvalidDigitPosition { 
    get { 
        for (int index = 0; index < this.positions.Count; ++index) { 
            if (!this.positions[index].Valid) return index; 
        } 
        return null;
    } 
} 

现在您不需要限制调用者首先调用AreThereInvalidDigits;只是让任何时候调用此方法合法。这似乎是更安全的事情。当你不做一些昂贵的检查以确认它们可以安全地打电话时爆炸的方法是脆弱的,危险的方法。

答案 1 :(得分:4)

我不明白为什么你不想编写一个练习“不应该发生的代码”的单元测试。

如果“不应该”,那你为什么要编写代码?因为您认为它当然可能发生 - 也许未来重构中的错误会破坏您的其他验证,并且此代码将被执行。

如果您认为值得编写代码,则值得对其进行单元测试。

答案 2 :(得分:4)

有时需要编写一小段无法执行的代码,只是为了安抚编译器(例如,如果一个总是抛出异常的函数,退出应用程序等等)从一个应该返回的函数调用一个值,编译器可能会坚持调用者包含一个“return 0”,“Return Nothing”等语句,这些语句永远不会执行。我不认为应该要求测试这样的“代码”,因为它可能是如果不严重破坏系统的其他部分,就无法执行。

一个更有趣的问题是代码,处理器在正常情况下永远不能执行,但存在的目的是最大限度地减少异常情况造成的伤害。例如,我编写的一些安全关键应用程序以类似的代码开头(实际应用程序是机器代码;下面给出的是伪代码)

  register = 0
  test register for zero
  if non-zero goto dead
  register = register - 1
  test register for zero
  if non-zero goto okay1
dead:
  shut everything down
  goto dead
okay1:
  register = register + 1
  if non-zero goto dead

我不确定如何在失败的情况下测试该代码。该代码的目的是确保如果寄存器遭受静电或其他损坏,或者否则无法正常工作,系统将安全关闭。但是,如果没有一些非常奇特的设备,我不知道如何测试这样的代码。

答案 3 :(得分:3)

测试目的的一部分是测试应该发生的事情以及可以发生的事情。

如果您的代码 永远不会执行,但可能在正确(或错误)条件下,您可以验证异常是否在正确的条件下发生(在MS的单元测试框架)通过以下方式装饰您的测试方法:

[ExpectedException(typeof(InvalidOperationException))]

答案 4 :(得分:0)

重新表述这个问题,基本上:当您已经遇到很多麻烦以确保系统无法首先进入无效状态时,您如何测试系统的无效状态?

它会使测试速度稍慢,但我建议使用反射将您的系统置于无效状态以进行测试。

另请注意,反射正是您的系统进入无效状态的一种可能方式,它会执行您认为不可能到达的代码。

如果您的代码确实无法访问,就我所见,在您的示例中并非如此,那么您应该删除代码。例如:

public int GetValue() {
   return 5;
   throw new InvalidOperationException("Somehow the return statement did not work!");
}
<块引用>

我也不想编写一个单元测试来练习不应该执行的代码。

这里的“应该”是什么意思?比如,它不应该被执行?当然,在测试中执行代码应该没问题。它应该在测试中执行。