【问题标题】:Should every possible branch in a method have a separate junit?方法中的每个可能分支都应该有一个单独的junit吗?
【发布时间】:2011-12-24 12:52:48
【问题描述】:

这更像是一个设计问题。

假设你有这样的方法(例如):

if (x == 5) {
  c = 1;
} else {
 if (z != 2) {
  b = 6;
} else {
   a = 3;
}

您认为为每个可能的分支设置一个junit 是最佳做法吗?即 testx5、test xnot5znot2、testxnot5z2 等,或者类似的东西:

void testMethod() {
// x is 5
test/assert code;

// x not 5, z not 2
test/assert code;

// x not 5, z is 2
test/assert code

// etc
}

编辑:为了清楚起见,我的目标是完整的代码覆盖率。我只是想知道我应该为每个分支进行新测试还是将它们组合在一个测试中的意见。感谢您的意见。

【问题讨论】:

    标签: java unit-testing testing junit


    【解决方案1】:

    JUnit FAQ 似乎表明最好用更少的断言进行更多的测试,因为 JUnit 只会报告测试方法中的第一个断言失败。使用一种方法,如果您破坏了 x = 5 案例,您将无法判断 x != 5 案例中的任何一个是否仍在工作。

    【讨论】:

    • 接受以了解我想知道的核心问题以及 JUnit 常见问题解答的链接。
    【解决方案2】:

    您所讨论的内容称为分支覆盖率

    传统观点认为,如果编写代码来覆盖该用例足够重要,那么编写测试用例来覆盖该代码也足够重要。所以这似乎表明 100% 的分支覆盖率是一个很好的目标(也意味着 100% 的语句覆盖率,但不一定是 100% 的循环或 100% 的条件覆盖率)。

    但是,您还需要在编写测试的工作量与获得这些测试的价值之间取得平衡。例如,如果您正在测试的代码有一个 try/catch 来捕获检查的异常,但该异常几乎从未被抛出(或在测试用例中难以引发),那么编写一个测试来覆盖该异常可能不值得你花时间。

    这就是为什么您在很多地方看到以一定百分比的测试覆盖率为目标是一个坏主意的原因,因为您最终编写测试用例是为了获得覆盖率,而不是为了发现错误。在您的示例中,是的,每个分支都值得拥有自己的测试用例。在每一段生产代码中,可能都没有必要。

    【讨论】:

      【解决方案3】:

      在单元测试中,您的目标是测试行为,而不是“代码”。想想你正在测试一个黑盒的方法,你想看看它是否能正常工作。您不知道它在内部是如何工作的,但您期望某些输入会产生某些结果。因此,您需要针对您期望代码如何工作的不同情况创建测试,就好像您对方法的内部实际上如何工作一无所知一样。所以你会编写像“applysDiscountToShoppingCart”和“addsDeliveryFeeToShoppingCart”这样的测试。

      现在,话虽如此,创建“边缘情况”也很有用,您可以在其中测试可能会破坏的事物(如空值、零值、负值、数据太大/太小等)以查看是否它也以预期的方式失败。通常要编写这些,您需要知道该方法实际上是如何工作的。如果您可以设计涵盖所有代码的测试,那就太好了! 100% 的测试覆盖率是一个明确的目标,但根据具体情况,它并不总是实用(或有用)。

      【讨论】:

      • 他在问他是否应该为一种方法的不同输入/用例编写单独的测试方法,或者只是在一种测试方法中放置多个断言。这并不能真正回答这个问题,尽管它本身就是很好的信息。
      • 这很好,我想我只是读了他的问题有点不同。最终,分支通常会针对不同的条件规定不同的行为,因此答案仍然是多种测试方法,每种测试方法测试一种行为。
      • 这似乎是传统智慧。但是我也有一些情况,我更喜欢在一个测试方法中放置多个断言,否则测试类会变得臃肿而收效甚微。就像在测试 hashCodeequals 方法时一样。你通常总是测试相同的东西,比如传递性和自反性,所以我真的不在乎我是否只看到第一次失败。一个错误的断言意味着该方法是错误的,所以我会在其余可能取决于该断言的其余部分之前修复它。编写测试很麻烦,可以理解有人想快速完成。
      【解决方案4】:

      特别是在构建服务器上,拥有许多不同的测试用例/功能更容易,因为它很容易识别哪个测试失败。另一个缺点是,如果第一个测试用例失败,测试用例将停止,您将不知道其他测试用例的结果。

      就我个人而言,当您必须进行大量复制粘贴来设置/解释测试用例时,这种好处就停止了,在这种情况下,我只会在同一个测试用例中做几个断言。

      【讨论】:

      • 如果您正在为您的设置做大量的复制粘贴,您不应该为您的测试类使用setUp() 方法吗?更何况,如果第一个断言失败了,那么你不知道其他断言是否有问题。
      • 我不是在谈论 jUnit 中的 setUp() 函数,而是在谈论测试用例描述被测试情况的​​部分(创建模拟等)。我将更新答案以反映您的优点,即您不会看到其他断言是否失败。
      猜你喜欢
      • 2013-10-29
      • 2017-08-16
      • 2019-04-09
      • 1970-01-01
      • 1970-01-01
      • 2016-01-14
      • 2010-10-24
      • 1970-01-01
      相关资源
      最近更新 更多