测试是软件开发不可分割的一部分。当你写出你的第一个程序的时候,你肯定会扔进去一些数据进行测试。
测试可以发现软件中的问题,而越晚发现问题,修复的成本就越高。发现问题只是测试的一部分功能,更重要的是要适应变化。没有测试的配套,你就无法对软件进行修改和变化。
测试也可以从侧面反映你的软件的设计是否良好。可测试性是软件的非常重要的非功能需求。
为什么要测试?
一个简单的测试包含以下的内容:
- 一个待测试的行为,例如API
- 测试的输入
- 可观测的输出或者行为
- 一个受控的环境,例如单个进程
除了能够支撑公司快速构建功能,适应变化。测试还能保证软件的安全性。当今的软件对人们生活的影响越来越大,软件故障所带来的代价和损失也越来越大,良好的软件测试能避免软件故障所造成的巨大损失。
谷歌的GWS(谷歌Web服务)在早期发展过程中问题很多,在引入对新的特性的自动化测试后,生产环境出错的几率下降了50%。
现代软件的规模越来越大,功能越来越复杂,自动化测试成为了“答案”。
测试代码的一些好处包括:
- 花更少的时间debug
- 对变更更具信心
- 改善文档
- 简化审视
- 更好的设计
- 快速,高质量的发布
设计测试套件
测试的规模
在谷歌,测试被要求尽可能的小。这里的小的意思不是指代码的行数,而是说测试的运行时间,消耗的资源,他所测试的内容。
在谷歌
- 小型测试意味着可以在一个进程中运行,所有测试的代码必须运行在同一个进程内,没有IO操作,没有Sleep
- 中型测试可以在一台机器中运行,可以运行多个进程,可以有进程间的IO,网络调用限制为Localhost
- 大型测试可以消耗任意多的资源。可以在多台机器中进行测试。
不稳定的测试(Flaky Test)代价巨大
我们希望所有的测试都是确定性的,然而在实际的运行过程中,因为受各种因素的影响,并非所有的测试都是确定性的,有些测试存在很多不确定性。带来不确定性的原因有:时钟时间,线程调度,网络时延等等。
谷歌不稳定测试的比例是大约0.15%。
好的自动化测试框架应该能够帮助识别这些不稳定的测试。降低不稳定测试的比例非常重要。
测试应该独立和自包含,没有外部依赖,并且可以重复的运行。
测试应该只包含需要的信息,尽可能的清晰。
测试代码不应该包含流程控制的过程,例如循环,if/else。越是复杂的测试,越是难以定位错误。
测试的范围
- 小范围测试,单元测试
- 中范围测试,集成测试
- 大范围测试,功能测试,端到端测试,系统测试
上图是一个合理的测试类型的金字塔分布。
而下图测试两个反模式的分布,冰激凌桶和沙漏模式。
测试对应我们的两个主要的目标,工程效率和产品的质量。
好的测试应该尽可能覆盖不同的规模和范围。
“碧昂丝”规则
我们常常被问起,当你招聘一个新员工的时候,什么行为和特质是你想要测试的?答案是“测试一切你不希望被破坏的东西”。我们称之为“碧昂丝”规则,软件测试也是一样的。
一个最重要的点就是测试系统故障的时候,你的软件的行为。
代码覆盖率
代码覆盖率指的是有多少行的代码被测试所运行,但值得注意的是,覆盖率100%并不意味你的测试都有效,也许所有的行数都被运行,但是并没有被验证。
设置一个测试覆盖率的门限意义并不大。例如80%,你认为这个是个下限,而工程师会以为这个是上限。
比起覆盖率,我们更应该关注的是多少的软件的行为被覆盖了。
谷歌规模的测试
谷歌采用单一代码仓的形式。谷歌所有的代码都存在一个仓库中,总共有约20亿行的代码。谷歌每周有大约2500万行的代码变动。
随着软件的发展,测试的代码也越来越多,越来越复杂,越来越慢。
测试变慢和不确定是主要要面临的挑战。我们要尊重大规模的测试用例。另外就是要提供高效的测试基础设施。
自动化测试的限制
自动化测试并非适用所有场景,
- 自动化测试难以进行某些质量判断
- 自动化测试无法胜任某些人类擅长的工作
- 自动化测试无法进行探索性的任务
结论
- 自动化测试是软件能够适应变化的基础
- 要让测试上规模,必须自动化
- 平衡的测试需要拥有各种类型的测试
- “如果你喜欢它,你需要测试它”
- 改变组织的测试文化需要花时间