什么样才算单元测试?谁该负责写?微软和谷歌这么说……
什么样才算单元测试?谁该负责写?微软和谷歌这么说……
原创 持续交付20 持续交付2.0
随着“DevOps”这个词在IT行业开始流行起来,就越来越多地听到有人讨论下面两个问题:
- 什么样的测试才算单元测试?
- 谁应该负责写单元测试?
事情就是这么巧,这两个问题都已经被万亿市值的牛X公司讨论过了……
难道你还不知道么?
一、 谷歌,自动化测试用例的 S / M / L
内容总结自《谷歌软件工程(2020版)》的第十一章,“自动化测试概述”
对于来自没有强大测试文化的组织的开发人员来说,将编写测试作为提高生产率和速度的一种手段的想法似乎是对立的。毕竟,编写测试可能需要同样长的时间(如果不是更长的话!)而不是实现功能会放在首位。相反,在Google,我们发现投资于软件自动化测试可以为开发人员的工作效率带来几个关键的好处:
- 调试更少
- 增强了对变革的信心
- 改进的文档
- 更简单的review
- 构思周到的设计
- 快速、高质量的发布
1、黑暗年代(2005年以前)
在谷歌早期,工程师驱动的测试通常被认为是无关紧要的。大家认为,我们依靠聪明人把软件做好。
有几个大型系统,编写并执行了一些大型集成测试,但大多数产品都是“敏捷裸奔”。
2005年,谷歌网络服务( Google Web Server ,简称GWS )的服务质量糟糕到了极点,工程师们从早到忙到晚,为了应付生产环境上出现的问题和故障,已经疲惫不堪。生产效能下降,发布质量变得越来越差,团队成员对发布越来越没有信心,发布得越来越慢。甚至在一段时间里,80 % 的发布都包含对用户有影响且必须回退的缺陷。
因此,当时负责 GWS 的 Tech Lead 要求必须写自动化测试,并执行持续集成实践。他们甚至专门安排了一名工程师做构建警察(Build Cop),负责确保每次构建失败,都能被快速修复。要么自己修复,要么找到那个破坏构建之人,要么回退代码。
一年之后,紧急修复的发布数量减少了一半。现在,GWS 有数万个自动化测试用例来保障代码质量。目前可以保持每天发布一次,并且用户可感知的缺陷相对很少。
这一事件可以被看做谷歌软件工程意识觉醒的标志性事件。它意味着,不可能一直依靠工程师的个体能力来避免产品缺陷。即使每个工程师都只是偶尔写出一个缺陷,那么当你有足够多的人在同一个项目上时,你仍旧会被越来越多的缺陷所包围。
2、2008 年以后
谷歌很早就学到的教训之一是,虽然工程师更愿意编写更大的系统级别的自动化测试用例。然而,与小型测试用例相比,这些系统级别的测试用例运行更慢、更不可靠、并且更难调试。
当忍受不了“系统级别测试的问题定位和调试”的痛苦时,他们就会问自己,“为什么我们不能一次只测试一台服务器呢?”或者,“为什么我们需要一次测试整个服务?我们是可以单独测试较小的模块的啊……”
最终,为了减轻痛苦,工程师开始编写越来越小型的测试用例。结果证明,这样的小型测试用例更快、更稳定,通常不那么痛苦。
这在公司周围引发了很多关于什么是“小”的意思?“小测试”意味着单元测试吗?那么集成测试呢?它们的测试规模是大,还是小?
我们得出的结论是:
每个自动测试用例有两个不同的维度:资源占用和验证范围。
资源大小:运行测试用例所需的资源:内存、进程和时间。
验证范围:正在被验证的特定代码路径的大小。
大小和范围相互关联,但他们又是截然不同的概念。
我们对小型、中型和大型的定义实际上是以测试基础设施对所执行的测试用例的资源约束来限定的。
但简而言之,小型测试是在单个进程中运行,中型测试可以在单台计算机上运行,而大型测试在他们想要的任何地方运行。
不使用传统意义上的“单元测试”或“集成测试”,是因为我们认为:测试集最重要的品质是速度和确定性,而不管测试的范围是什么。
谷歌主要是对全系统端到端验证而保留那些较大的测试,这些测试更多的是验证配置而不是某个代码片段,以及那些无法使用测试替身对象的遗留系统测试。
他们经常会将大型测试与小型或中型测试隔离开来,只在创建发布分支进行构建和发布过程中运行大型测试,以免影响开发人员的工作流程。
二、 微软的自动化测试用例分级:从 L0 到 L3
1. 黑暗时代 ( 2010 年以前)
众所周知,微软是最重视自动化测试的软件公司。自上个世纪九十年代开始,微软的测试部门就有专门的两个职位,它们是:
(1)Software Design Engineer Test (SDETs): 负责开发自动化和测试基础设施。
(2)Software Test Engineer (STEs) :运行自动化测试,和执行手工测试。
那时,他们的开发人员与测试人员的比例达甚至达到了 1 : 1 。然而,这种做法在现今根本无法正常运作。
但它真的有用吗?一句话——没有。我们开始看到一些问题,但被产品的商业成功掩盖了一些。到了90年代末,这些问题就水落石出了。开发人员把代码扔到了SDETs面前。SDETs把测试自动化扔给了STEs。我们的对策是不断壮大STEs,尤其是供应商。STEs在测试领域的晋升机会有限。维护这种配置是非常昂贵的。测试成为了一个瓶颈,导致了产品的延迟,但我们还是无法看穿它。
—— 参见《Evolving Test Practices at Microsoft - Azure DevOps》
2. 开启DevOps时代
在微软开始DevOps之旅后,开始寻求方法,改变这种状态。在2015年,团队制定了新的质量愿景。其中核心的部分就是把整个测试组合向源头移动。
以前,我们的自动化测试分类是基于测试执行的时间,例如 nightly run (NAR) 和full automation run (FAR), 等。现在,我们基于对外部的依赖,重新划分了自动化测试的类别。如下所示。
L0/L1 – Unit tests
L0 – 这个级别的测试是数量最多,在内存中快速执行的单元测试。一个 L0 测试用例对大多数人来说,就是那个最经典的单元测试定义,它只依赖于被测试的代码,没有其它依赖。
L1 – 这个级别的测试可能需要二进制集成包 加上(数据SQL 或者file system)。
L2/L3 – Functional tests
L2 – 这个功能测试类是针对“可测试的”服务部署执行。它算是一个功能测试类,需要进行服务的部署,但可能会以某种方式将关键的服务依赖关系断开。
L3 – 这是在生产环境中执行的受限集成测试类别。它们需要完整的产品部署。
微软的 VSTS 团队花了两年半的时间,才将它以大型系统测试(上图中的橙色部分)为主转变成以 L0 测试用例(蓝色部分)为主的状态。
当然,微软 VSTS 不再有写自动化测试的 SDTE ,也没有维护自动化测试的 STE 了,这些工作全部由开发工程师代劳。