长期以来,我一直试图在我所从事的每个项目中加入测试。
它们中的每一种都因其试图覆盖的范围而与其他的不同。从完全脱离外部依赖的小单元代码,到作为黑盒子测试的整个应用程序,就像最终消费者在使用它一样。
我不打算深入讨论这背后的语义,或者集成测试在哪里结束,端到端测试在哪里开始,因为这常常是一条模糊的线。
我想把重点放在单元测试上,以及一个经常使用的做法(甚至在我自己的测试中),我已经读过(并且感觉)好几次了,它可能不是最好的方法。
什么是单元测试
根据维基百科,单元测试是一种 "对源代码的各个单元[...]进行测试以确定它们是否适合使用的方法"。
虽然这看起来是一个清晰的定义,但实际上它是相当开放的,因为在单元测试的背景下,什么是 "单元"?
很多时候,在面向对象的编程项目中,人们把一个类作为一个单元来测试,并为每个类创建一个单元测试。在其他范式中,比如函数式编程,你可能认为一个函数是要测试的单元,或者你决定将一组函数放在一个模块中。
这对于本文的目的来说并不重要。真正重要的是要考虑到,为了正确地 "单元测试 "一个 "源代码单元",你必须把它与该单元的任何依赖关系隔离开来。
实现这一目标的较好方法之一是遵循**依赖性注入原则**。如果没有这个原则,你可能最终会试图黑掉自己,以便将 "单元 "从其依赖关系中分离出来。
你可以在我的博客中找到关于依赖注入的其他文章。
替换依赖关系
在对源代码的一个单元进行单元测试时,一个非常常见的做法是用测试替身来替换它的依赖关系,以便在测试时正确地隔离每个单元。
测试替身基本上是与被测对象的依赖关系兼容的对象,它们是为了测试的目的而创建的,并具有一些有用的功能,如捕获它们是如何被调用的,或者当它们被调用时应该返回什么。
当你使用这些测试替身之一来断言它们是如何被调用的,作为测试本身的一部分,你在使用它们作为 "间谍"。
过度信任的间谍
所有这些介绍和语境化都有一个目的。
我发现我自己(和其他人)处于这样一种情况:你准备一个测试替身以某种方式行事,然后你调用你的被测对象,然后通过监视它来断言你对测试替身如何被调用的期望是正确的。
这有时感觉是错误的,因为你似乎是在测试测试替身而不是被测对象。
我读过很多次,在单元测试中,我们实际上不应该知道被测对象的内部实现,而只是测试在某些输入的基础上,返回预期的输出(所以,相信其公共API)。
然而,在有些情况下,一些函数/方法并没有真正的输出,我们只能通过窥视测试的替身来验证一切是否正常。
因此,在一天结束时,我对这种做法的感受是复杂的。
结论
可悲的是,我无法说出一个明确的结论。这实际上是一篇开放的文章,我很想以评论的形式获得你的观点。
看看其他人是如何面对这个问题的,如果你使用其他类型的测试替身而不是间谍,或者如果你只是接受这个影响,这将是非常好的。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理