测试一个巨大的分解方法及其内部辅助方法

时间:2012-10-15 17:10:15

标签: java unit-testing testing tdd

我最近一直在写很多单元测试。我找不到一个干净的解决方案......

让我们说你有一个方法的野兽:

public void bigMethod() {
    // a lot of code goes in here
}

为了让您的生活更轻松,您的代码更清洁,您通常会将这样的野兽分解为更小的内部方法:

public void bigMethod() {
    a();
    b();
    c();
    // etc.
}

您可以独立测试所有内部方法(a(),b(),c()等)。问题是bigMethod()也应该进行测试,但它唯一要做的就是链接一些其他方法的调用,并且已经进行了彻底的测试!

你如何处理这种情况?你不能只留下bigMethod()未经测试,因为你需要确保在那里调用a(),b()和c()IN PROPER ORDER。但是为bigMethod()编写测试会导致大量的重复测试。每次减少这种重复都是很麻烦的,因为你会做那个OFTEN。

我想到的一个想法是:

public void bigMethod() {
    helperA.a();
    helperB.b();
    helperC.c();
    // etc.
}

在这种情况下,您测试每个帮助程序类,然后确保bigMethod()在inOrder中调用它们。很好,很干净,但在项目中引入了很多非常小的课程。

帮助测试忍者!

4 个答案:

答案 0 :(得分:2)

通常,最佳做法是让您的单元测试测试bigMethod(),因为它是公开的方法,并且测试不知道内部私有帮助方法。

如果您重构bigMethod()的内部实现,那么对类的内部工作原理过多的测试往往很脆弱,必须进行大量修改。正如您所指出的那样。

所以我的建议是重写你的测试以专注于bigMethod(),然后不必担心代码重复。

答案 1 :(得分:1)

你无法找到一个好的解决方案的原因是将方法分成连续的部分并没有真正改善事情。事实上,它可能使调试更难,因为你必须跨方法边界共享状态,可能使用实例变量。

我知道这不是您想要听到的答案,但如果您真的想要改进代码,您将退后一步并重新分析bigMethod做什么并重写它,分离其各种功能到具有适当接口的类。理想情况下,您还要重构bigMethod被调用的位置,但我意识到这可能超出了您分配的任务的范围。

答案 2 :(得分:1)

首先,如果a()b()c()是公开方法,则以下所有文字都有意义。

bigMethod()测试重点关注内部调用的后果,意味着调用a()后跟b()并后跟c()

完全是另一种情况,而不是单独测试这些方法。

所以根据我的说法,为bigMethod()添加一个特定的测试并没有发现一些重复,而是一个新的有趣案例来检查:内部调用的协作

我会保留你的第一个版本的代码并测试它:)

实际上,这似乎是与项目交付类似的常见问题:

假设三个项目分别开发并最终收集它们。

三种可能的做法:

  • 独立测试三个项目
  • 等待仅测试三个项目的混合
  • 测试这两种方式!

从逻辑上讲,第三种可能性是希望的;单元测试也一样。

a()b()c()可能会出现意外行为,但不会阻止bigMethod()获得预期结果,反之,bigMethod()可能会有尽管独立方法的测试成功,但出乎意料的结果。

答案 3 :(得分:0)

我认为解决方案没有清楚地显示给您的原因是因为您没有真正考虑并定义a()b()c()的性质。您肯定需要弄清楚以下a()b()c()中的哪一个:

  1. bigMethod()的内部,不可分割的子部分,在其之外没有任何意义。

  2. 作为班级公共合同一部分的方法有助于其凝聚力,可以独立于外部进行调用。

  3. 毕竟不属于班级的方法。将它们拿出来将有利于课堂的凝聚力,使它们更容易重复使用。

  4. 一旦你想出来,如何测试它们的答案很容易。

    1. 只需将bigMethod()作为原子块进行测试,不要试图调整其内部子部件的可见范围,以使其可测试。

    2. 独立测试a()b()c()。然后要测试bigMethod(),请使用部分模拟,然后验证是否以正确的顺序调用子方法。您还可以测试bigMethod()的结果,使其成为集成测试而不是单元测试,并提出您提到的重复问题。

    3. 独立测试a()b()c()。然后要测试bigMethod(),请使用模拟对象,并验证bigMethod()是否正确地与这些对象进行对话。