关于测试的各种奇幻形状
金字塔、蜂窝、奖杯以及单元测试的意义
2021年6月2日
最近,推特和其他平台上关于团队如何划分测试工作出现了很多讨论。特别是,Tim Bray 有说服力地论证了认真对待自动化测试的重要性。任何熟悉我写作的人都知道我非常同意他的观点。
他在文章中提到的一个观点是指这两张图片
这两个“奇形怪状的斑点”是对旧的 测试金字塔 图像的反应。
这些图像的目的是表明我们应该在各种类型的测试上投入多少精力,特别是单元测试和更广泛的测试之间的平衡。金字塔认为,你应该将大部分测试作为单元测试,而蜂窝和奖杯则认为,你应该进行相对少量的单元测试,而主要关注集成测试。
我对这场讨论的第二个最大问题是,由于人们对单元测试和集成测试之间的区别并不清楚,导致讨论变得不透明。
“单元测试”和“集成测试”这两个术语一直都很模糊,即使按照大多数软件术语的模糊标准也是如此。我最初的理解是,它们主要是一个组织问题。让我们回到大型瀑布式软件项目的时代。我正在开发一段代码,这可能需要几个月的时间。我可能独自工作,也可能在一个小型团队中工作。无论哪种方式,我认为这段代码是一个概念上的单元,我们可以相对独立地与其他代码进行开发。一旦我们完成编码,就可以将其交给单元测试团队,他们会单独测试该单元。经过一两个月的时间来使这些测试通过后,我们就可以将其与其他代码集成,并针对系统更大的部分(甚至整个系统)进行集成测试。关键的区别在于,单元测试独立地测试我的/我们的代码,而集成测试则测试我们的代码如何与单独开发的代码一起工作。
如今,许多人是在使用由 Kent Beck 作为 极限编程 的一部分而开创的 Xunit 系列测试工具时接触到单元测试的。Kent 使用“单元测试”来表示开发人员在日常工作中编写的测试。
程序员编写单元测试,以便他们对程序运行的信心可以成为程序本身的一部分。客户编写功能测试,以便他们对程序运行的信心也可以成为程序本身的一部分。
请注意,在 Kent 的最初表述中,“单元测试”指的是程序员编写的任何测试,而不是单独的测试团队编写的测试。在 C3,当我们编写单元测试时,我们通常会关注单个类的行为。但我们会设置一个测试夹具,用所有必要的依赖项创建该对象,以便它可以执行其方法。这些其他对象也会执行,但我们假设对于此测试,所有其他代码都正常工作(通常其他代码有自己的测试)。
我记得关于“单元测试”这个用法的讨论很多。一位测试专家强烈批评 Kent 的这种用法。我们问他如何定义单元测试,他的回答类似于“在我的培训课程的第一天早上,我介绍了 24 种不同的单元测试定义”。
从 XP 启发的单元测试的早期开始,就有人不喜欢“单元测试”这个术语,并建议使用“微测试”或“程序员测试”等名称来代替。
对许多人来说,最大的问题是这些依赖对象。如果我正在测试一个订单对象,并且它与一个客户对象协作,那么我对订单对象的测试可能会因为客户对象中的错误而失败。这导致了编写单元测试的不同风格,其中任何协作对象都被替换为模拟、存根或其他类型的 测试替身。我发现用“社交”和“孤独”来描述这些类型的 单元测试 很有用。 [1]
与这种区别交织在一起的是两种 XP 单元测试实践的兴起,我称之为经典和模拟主义。经典 XP 单元测试遵循我们最初使用的社交方法,而模拟主义风格则偏爱孤独的测试。
因此,回到金字塔与蜂窝,当我阅读蜂窝和类似形状的倡导者时,我通常会听到他们批评过度使用模拟,并谈论这会导致的各种问题。由此我推断,他们对“单元测试”的定义具体来说是我所说的孤独单元测试。同样,他们对集成测试的概念听起来非常像我所说的社交单元测试。这使得金字塔与蜂窝的讨论变得毫无意义,因为我所听到的关于测试金字塔的任何描述都认为单元测试是社交的和/或孤独的。
由于 集成测试 的定义,这种语义图变得更加混乱,这使得“单元测试”看起来定义得很严格。这里需要记住的是,当有人开始谈论各种测试类别时,要深入了解他们对这些词的含义,因为他们可能不像你之前阅读的人那样使用它们。
如果你对我的精心写作给予了应有的关注,你会注意到我之前说过,对单元测试和集成测试缺乏清晰度是我对蜂窝/金字塔讨论的第二个最大问题。我最大的问题可以用以下引文概括。
人们喜欢争论要编写多少百分比的哪种类型的测试,但这是一种干扰。几乎没有团队编写表达能力强的测试,这些测试可以建立明确的边界,快速可靠地运行,并且只因有用的原因而失败。专注于此而不是其他。
脚注
1: Jay Fields 提出了“孤独”和“社交”这两个术语