黑匣子单元测试

时间:2012-05-13 10:09:58

标签: unit-testing

在我的上一个项目中,我们进行了单位测试,几乎100%cc,因此我们几乎没有任何错误。 但是,由于单元测试必须是白盒(你必须模拟内部函数来获得你想要的结果,所以你的测试需要知道你的代码的内部结构)任何时候我们改变函数的实现,我们不得不也改变测试。 请注意,我们没有更改函数的逻辑,只是实现。 这非常耗时,感觉好像我们的工作方式不对。 由于我们使用了所有适当的OOP准则(特别是封装),每次我们更改实现时,我们都不必更改其余的代码,但必须更改单元测试。 感觉好像我们正在为测试服务,而不是为我们服务。

为了防止这种情况,我们中的一些人认为单元测试应该是黑盒测试。 如果我们创建整个Domain的一个大模拟并为一个地方的每个类中的每个函数创建一个存根,并在每个单元测试中使用它,那么这是可能的。 当然,如果特定的测试需要调用特定的内部函数(就像确保我们写入DB一样),我们可以覆盖我们的存根。

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

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

总之,有两个问题:

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

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

6 个答案:

答案 0 :(得分:11)

您需要不同类型的测试。

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

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

  • 验收测试应该完全是黑盒子并且由功能要求驱动(因为您的用户会对它们进行短语)。尽可能端到端,不知道所选实现的内部。黑盒测试的教科书定义。

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

答案 1 :(得分:6)

  

因此我们几乎没有任何错误

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

黑盒测试可能听起来很吸引人,但事实是你几乎总是需要知道测试类的内部工作的一部分。 提供输入,验证输出实际上仅适用于简单情况。大多数情况下,您的测试需要至少掌握一些测试方法的知识 - 它如何与外部协作者交互,调用哪些方法,以什么顺序等等。

模拟和SOLID设计背后的整体思路是避免依赖项实现更改导致其他类测试更改/失败的情况。相反,如果您更改测试方法的实现细节,那么应该更改它的实现细节测试。这没什么不常见的。

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

答案 2 :(得分:6)

tl; dr版本:

  1. 黑匣子单元测试正是应该如何进行单元测试。
  2. 黑匣子单元测试正是应该如何进行单元测试。正确的TDD实践就是这样做的。
  3. 完整版。

    绝对没有必要测试对象的私有方法。它对代码覆盖率也没有影响。

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

    如果你做了常见的错误并且静态地依赖于其他实体类或者更糟糕的是,在来自不同应用层的类中,当你需要在测试中检查很多东西时,你将发现自己处于这种情况是不可避免的。并为它准备了很多东西。为了解决这个问题,依赖注入原则和Law of Demeter存在。

答案 3 :(得分:2)

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

单元测试应该是低级别的,但应该测试结果而不是测试的方式。当实现更改导致大量测试更改时,这意味着您不是测试需求,而是实际测试实现。

拇指有几个规则 - 例如“不测试私有方法”并使用模拟对象。

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

我建议您继续编写单元测试 - 只是学习如何使它们更健壮,更不易碎。

答案 4 :(得分:1)

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

答案 5 :(得分:0)

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

Testing Web-based applications: The state of the art and future trends (PDF)