17

在我的上一个项目中,我们进行了几乎 100% cc 的单元测试,因此我们几乎没有任何错误。

但是,由于单元测试必须是白盒(您必须模拟内部函数才能获得所需的结果,因此您的测试需要了解代码的内部结构)任何时候我们更改函数的实现,我们都必须也改变测试。

请注意,我们没有更改函数的逻辑,只是更改了实现。

这非常耗时,感觉好像我们的工作方式不对。由于我们使用了所有正确的 OOP 指南(特别是封装),每次我们更改实现时,我们不必更改其余代码,但必须更改单元测试。

感觉好像我们是在为测试服务,而不是他们为我们服务。

为了防止这种情况,我们中的一些人认为单元测试应该是黑盒测试。

如果我们为整个域创建一个大模拟,并在一个地方为每个类中的每个函数创建一个存根,并在每个单元测试中使用它,那将是可能的。

当然,如果一个特定的测试需要调用特定的内部函数(比如确保我们写入数据库),我们可以覆盖我们的存根。

因此,每次我们更改函数的实现(例如添加或替换对帮助函数的调用)时,我们只需要更改我们的 main big mock。即使我们确实需要更改一些单元测试,它仍然会比以前少得多。

其他人认为单元测试必须是白盒测试,因为您不仅要确保您的应用程序在特定位置写入数据库,还要确保您的应用程序不会在其他任何地方写入数据库,除非您特别期望它至。虽然这是一个有效的观点,但我认为不值得花时间编写白盒测试而不是黑盒测试。

所以总结一下,有两个问题:

  1. 您如何看待黑盒单元测试的概念?

  2. 您如何看待我们想要实施该概念的方式?你有更好的想法吗?

4

6 回答 6

11

您需要不同类型的测试。

  • 应该是白盒测试的单元测试,就像你做的那样

  • 集成测试(或系统测试),测试使用系统实际实现的能力及其与外部层(外部系统、数据库等)的通信,应该是黑盒样式的,但每个都针对特定的功能(例如 CRUD 测试)

  • 验收测试应该是完全黑盒的,并且由功能需求驱动(正如您的用户所说的那样)。尽可能端到端,并且不知道您选择的实现的内部。黑盒测试的教科书定义。

请记住,在大多数情况下,代码覆盖率是没有意义的。您需要高行覆盖率(或方法覆盖率,无论您的计数方法是什么),但这通常是不够的。您需要考虑的概念是功能覆盖:确保您的所有需求和逻辑路径都被覆盖。

于 2012-05-13T10:27:02.603 回答
6

结果我们几乎没有任何错误

如果你真的能够做到这一点,那么我认为你不应该改变任何东西。

黑盒测试在纸面上听起来很吸引人,但事实是你几乎总是需要了解被测试类的部分内部工作原理。在现实中提供输入,验证输出仅适用于简单的情况大多数时候,您的测试至少需要了解一些被测方法的知识——它如何与外部协作者交互、它调用什么方法、以什么顺序等等。

模拟和 SOLID 设计背后的整个想法是避免依赖实现更改导致其他类测试更改/失败的情况。相反,如果你改变了被测方法的实现细节,那么就应该改变它测试的实现细节。这并不少见。

总的来说,如果你真的能够实现几乎没有错误,那么我会坚持这种方法。

于 2012-05-13T10:19:20.960 回答
6

tl;博士版本:

  1. 黑盒单元测试正是应该如何进行单元测试。
  2. 黑盒单元测试正是应该如何进行单元测试。正确的 TDD 实践正是这样做的。

完整版本。

完全不需要测试对象的私有方法。它也不会影响代码覆盖率。

当您对一个类进行 TDD 时,您会编写测试来检查该类的行为。行为通过该类的公共方法来表达。你永远不应该关心这些方法是如何真正实现的。谷歌人描述的比我能做到的要好得多:http: //googletesting.blogspot.ru/2013/08/testing-on-toilet-test-behavior-not.html

如果你犯了常见的错误并且静态地依赖于其他实体类,或者更糟糕的是,依赖于来自不同应用层的类,那么你不可避免地会发现自己处于需要在测试中检查很多东西并准备一个很多东西。为了解决这个问题,存在依赖注入原理和得墨忒耳定律

于 2014-01-31T09:52:02.180 回答
2

我认为你应该继续编写单元测试——只是让它们不那么脆弱。

单元测试应该是低级的,但应该测试结果,而不是如何完成。当实现更改导致大量测试更改时,这意味着您实际上是在测试实现而不是测试需求。

有几条经验法则——比如“不要测试私有方法”和使用模拟对象。

模拟/模拟整个域通常会导致与您尝试完成的相反 - 当代码行为发生变化时,您需要更新测试以确保您的“模拟对象”行为相同 - 它变得非常困难非常快随着项目复杂性的增加。

我建议你继续编写单元测试——只要学习如何使它们更健壮和不那么脆弱。

于 2012-05-13T10:26:08.583 回答
1

“结果我们几乎没有任何错误”——所以保持这种状态。令人沮丧的唯一原因是维护单元测试的必要性,这实际上并不是一件坏事(替代方案更糟)。只是让它们更易于维护。Roy Osherove 的“单元测试的艺术”以这种方式给了我一个良好的开端。所以1)不是一个选择。(例如,这个想法本身与 TDD 的原则相矛盾) 2)使用这种方法,您将遇到更多的维护麻烦。单元测试的理念是从其他系统中剔除 SUT,并使用存根作为输入和模拟作为输出(信号?)模拟现实生活情况(或者我只是没有抓住“我们整个领域的一个大模拟”的想法)。

于 2012-05-14T10:46:38.790 回答
0

有关黑色、白色和灰色框和决策表的详细信息,请参阅以下文章,该文章解释了所有内容。

测试基于 Web 的应用程序:最新技术和未来趋势 (PDF)

于 2013-08-08T19:16:31.063 回答