持续集成的目的是为组织提供快速反馈. 而反馈的唯一途径则是测试. 手工测试无法满足集成的频繁程度和时效性的要求, 我们需要自动化测试, 并将其内建在开发过程当中.
另一方面, 持续集成可用于辅助实现持续部署. 在互联网时代, 将产品的新功能和缺陷修复部署到生产环境中的节奏越来越快, 越来越频繁. 要安全无风险的部署, 完善的自动化测试不可或缺.
因此从某种程度上来讲, 自动化测试跟持续集成基础设施同样重要, 如果不是更重要的话. 我们需要建立初步的测试策略, 包括测试分类, 典型场景和技术, 所用工具等. 该策略应随时间演化.
1. 测试象限
在测试策略领域, 比较常用的工具是测试象限, 见下图.
在测试象限中, 把所有测试按两个维度进行了划分, 因此总共有四种组合. 在原图中, 除右上角象限外, 其它三个象限的测试都可以进行自动化. 而随着技术的发展, 右上角象限的测试也部分的可以进行自动化. 那么问题自然而然出现: 应该从哪入手? 在资源受限的情况下, 重点应该投资哪类测试以获得最大收益?
2. 测试金字塔
在回答这个问题之前, 我们先介绍一个概念, 测试金字塔. 见下图.
从上往下, 成本更低, 效率更高, 更贴近技术实现; 从下往上, 则成本高, 效率低, 但更接近真实业务需求. 一个一般性的结论是:
尽可能使用大量低成本的测试来构筑安全网, 辅之以少量高成本但更接近业务的测试.
金字塔的用意是表明不同类型的测试在整体测试中所占的比例. 那么回到我们上面的问题: 应该从哪入手? 在资源受限的情况下, 重点应该投资哪类测试以获得最大收益? 我们发现测试金字塔依然回答不了上述问题. 但它提供了一个很好的思路, 就是考虑投资回报率, 投入产出比. 真正的答案取决于我们面对的产品类型, 及其它众多因素. 有一些一般性的原则如下.
2.1 遗留系统的测试策略
对于没有自动化测试来覆盖的产品代码, 我们称之为遗留代码, 无论是几年前写的还是昨天写的. 对于没有自动化测试覆盖的系统, 我们称之为遗留系统, 无论是几年前发布的还是昨天发布的. 而很多公司都有众多的遗留系统.
对于这类系统, 往往牵一发而动全身. 而且我们也无法在短时间内建立起完善的安全网. 我们的出发点应该是尽可能少的测试覆盖尽可能多的代码, 重点是关键路径, 关键业务场景. 所以针对这类系统:
我们应该从面向业务的高层测试入手, 比如验收测试, 场景测试, 以期尽快保护好关键业务功能.
更详尽的策略, 可参见 ThoughtWorks 工程师胡振波的<<以自动化测试撬动遗留系统>>一文.
2.2 新产品测试策略
对于新产品开发, 推荐的测试策略是从单元测试等低成本测试类型入手, 反馈迅速, 定位问题直接. 可保证每一个新功能自身的功能正确性. 随着功能增多, 可逐步增加功能间的集成测试.
2.3 创新产品测试策略
在新产品中, 有一类产品越来越突出, 就是试验性创新产品. 这类产品的显著特点是面临极大的不确定性, 包括随时可能变化的市场和用户, 随时丢弃的功能. 为这类产品按部就班的添加完善的测试, 有极高的概率变得不合算, 因为测试代码很可能伴随着被测功能整个被扔掉. 而简单的不写测试的策略, 也很容易将自己置于BUG 频出, 降低用户体验, 难以安全添加新功能的境地. 我们需要从更高层面考虑该类产品的测试策略. 一个一般性的策略是:
尽可能以非编程实现的方式验证产品市场和用户需求.
这样的话可以最大程度的提高”进入到开发阶段的需求就是市场真正需要的功能”的概率. 但我们仍然需要应对比传统软件市场更剧烈和频繁的需求转向. 而这类场景中, 最重要的不是产品质量(如果我们不知道谁是用户, 我们也不知道什么是质量), 而是验证关于产品功能的关键假设. 测试的任务就变成了保护为验证产品关键假设而开发的那些功能的用户体验. 这样我们依然回到价值分析, 回到投入产出比分析上来:
我们应该关注为关键假设而开发的功能, 为其编写能够保护用户流畅体验该功能所必须的最少量测试.
3. 测试工具
尽管我们可以定义各种测试策略, 但在实际操作中, 依然需要有经验丰富的开发者在团队中进行指导, 包括工具的使用.
自动化测试工具发展到今天, 市面上流行的测试工具基础特性差别不大. 比如对于 Java 来讲, JUnit 和 TestNG 都是可用的选择. 除编写和运行测试的基础功能外, 对于工具的选择, 我们可以从以下几个方面进行考虑.
3.1 表达能力
测试工具应能让使用者以直观的描述来表达意图, 而不陷入晦涩的实现细节. 具体的特性可能包括:
- 与需求文档结合, 以自然语言描述测试, 但本身又是可执行的. 比如 Concordion.
- 数据驱动, 能够将测试数据从测试实现中独立出来, 通过不同数据来表达不同的测试场景和用例.
- DSL 风格的测试表述.
3.2 促进协作的能力
测试工具应该能够促进各个角色的协作, 而不只是开发人员使用, 或者测试人员使用. 测试分两个层次, 验证做了正确的事, 和正确的做了某事. 要达到这两个目的, 需要业务人员, 开发人员, 和测试人员通力协作. 理想的测试工具应该能让所有角色的人员都工作在同一份测试用例上, 使用统一的彼此都可以理解的语言, 每个人都可以从自己的角度提供信息. 比如下面的测试用例:
上面的用例中包含了该功能(Password Validation)的意图, 具体规则描述, 以及针对每一条规则的验收测试. 绿色背景的测试结果表明这是一份可执行的测试用例, 且全部通过. 这同时又是一份需求说明文档, 所有角色的人员都可以理解, 并参与编写.
基于以上的考虑, 我们推荐以下几种工具作为起点.
初始测试工具列表
应用领域 | 工具 |
---|---|
面向技术的测试(Java) | JUnit |
面向业务的测试(Java) | Concordion |
面向技术的测试(JavaScript) | Karma + Jasmine |
面向业务的测试(JavaScript/AngularJS) | Protractor |
面向技术的测试(Android) | Robolectric |
面向业务的测试(Android) | Calabash |
iOS 测试 | XCTest |