【问题标题】:Unit testing a public method that calls multiple private methods对调用多个私有方法的公共方法进行单元测试
【发布时间】:2019-06-19 05:31:06
【问题描述】:

我想测试一个调用多个私有方法的公共方法。我从以前提出的问题的所有答案中读到的内容各不相同。有人说如果遇到这种情况,可能是结构不对,或者调用其他方法没有任何逻辑的方法不应该测试等等。

我不清楚的是,我应该模拟这些私有方法(使用 PowerMock 或任何基于反射的库)并对这个方法进行单元测试,还是应该提供不同类型的输入,以便所有情况都测试,但私有方法也会被调用。在后一种情况下,是否仍然是单元测试,因为我也会调用其他方法。

class ClassToTest {

    public void publicMethod (Argument argument) {
        try {
            privateMethod1();

            privateMethod2(argument);

            privateMethod3();
        } catch (Exception ex) {
            privateMethod4();
        }
    }

}

【问题讨论】:

  • 后者。甚至不要尝试模拟私有方法。是的,它仍然是单元测试。
  • 一个问题,大多数人说在单元测试中使用反射的私有方法模拟/调用是一个糟糕的主意。通过反射设置私有字段值怎么样?还是“不能接受”吗?

标签: java unit-testing junit


【解决方案1】:

首先,重要的是要了解,尝试使单元测试套件完全独立于实现细节可能会导致测试套件效率低下,也就是说,测试套件不适合查找所有可以找到的错误。而且,发现错误是测试的一个主要目标(参见 Myers、Badgett、Sandler:软件测试的艺术,或 Beizer:软件测试技术等)。

同一接口的替代实现具有不同的潜在错误。对 fibonacci 函数的迭代/递归实现的测试与使用 Moivre/Binet 的封闭形式表达式的实现或查找表实现的测试看起来不同。

然而,单元测试也有次要的目标。其中之一是避免您的测试在实现细节发生变化时不必要地中断。因此,测试不应不必要地依赖于实现细节。始终尝试首先创建与实现无关的有用测试,然后添加特定于实现的测试。对于后者,测试内部(私有)方法(例如通过使它们包可见)也可以是一个有效的选择 - 只要您知道缺点(如果内部方法被重命名、删除等,则需要维护测试代码.) 并权衡它们的优势。

其次,您很可能不应该模拟私有方法,而只是将它们用作测试的一部分。我说很有可能,因为这取决于方法。模拟应该是有原因的(否则应避免)。充分的理由是:

  • 您无法轻松地使依赖组件 (DOC) 的行为符合您的测试预期。
  • 调用 DOC 是否会导致任何非确定性行为(日期/时间、随机性、网络连接)?
  • 测试设置过于复杂和/或维护密集(例如,需要外部文件)
  • 原始 DOC 为您的测试代码带来了可移植性问题。
  • 使用原始 DOC 是否会导致构建/执行时间过长而无法接受?
  • 是否存在导致测试不可靠的 DOC 稳定性(成熟度)问题,或者更糟糕的是,DOC 甚至还没有可用?

例如,您(通常)不会模拟 sin 或 cos 等标准库数学函数,因为它们不存在上述任何问题。您必须判断这是否也适用于您的私有方法。

【讨论】:

  • 非常感谢德克,我明白了。第二个要点是我需要的。我的私有方法以一种确定性的方式运行,基于只有一个返回值的输入。但是有一个问题,大多数人说在单元测试中使用反射的私有方法模拟/调用是一个糟糕的主意。通过反射设置私有字段值怎么样?还是“不能接受”吗?
  • 正如我在回答中提到的,对实现细节的任何依赖都以更脆弱的测试为代价。好处可以超过这个价格,所以我一般不会说它是“不可接受的”。您通常可以通过将丑陋隐藏在辅助方法后面来降低风险。另一方面,辅助方法也可以作为通过公共 API 分解复杂设置的选项,从而保持更简洁。但是,对情况 A 正确的事情对情况 B 可能是错误的 - 这完全取决于特定情况的权衡。
猜你喜欢
  • 1970-01-01
  • 2012-09-20
  • 1970-01-01
  • 2016-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多