做持续集成 (CI) 工作以来,我越来越感受强烈的一点是 CI 不是孤立存在的。CI 与软件开发存在密切联系,这是众人皆知的。然而,CI 与软件测试存在的密切关系,提及者却不多。
"皮之不存,毛将焉附"。如果没有软件开发,CI 将完全没有存在意义;如果没有软件测试,CI 将黯然失色许多。这篇文章,就来聊聊软件测试在 CI 中的角色。
CI 存在的最基础的需求,是把来自各个开发者的代码,或者来自各个开发团队的软件模块,以某种方式编译,组装在一起,成为一个完整的软件包或者产品,交付给下游的测试部门或者用户。
基于这个需求,CI 的一部分任务可以简单解释为: 获取开发者提交的代码,将代码编译成一个个的模块,然后将多个模块组装成一个完整的包。这个过程中,还涉及到编译环境管理,软件版本管理,分支管理,模块之间的接口管理等技术问题。
虽然这部分内容很重要,但是如果仅仅只有这部分内容,CI 无疑不会有它在现代敏捷开发和 DevOps 中如此重要的地位。CI 的重要性,还体现在它拥有另一部分不容忽视的内容,那就是软件测试。
测试为什么会成为 CI 的重要内容?这实际上是来自软件质量方面的要求。根本原因在于,CI 要交付的不是任意的一个包,而要是一个好包。毕竟,交付一个不工作的包,意义何在?
那么,什么样的包才是好包?口说无凭,需要证据。证据来自测试结果。一般认为,通过软件测试检验的包,才是一个相对较好的包。
具体来说,光有编译和基本的版本检查还不够,我们还需要利用各种各样的基于软件需求或者代码实现所设计的测试用例来保障软件包的质量。
并且,这里的测试往往不是单一的,而是覆盖 CI 编组包全过程的。例如,构建好一个模块之后,最好在针对这个模块的测试通过后,我们才继续组包;组装好一个包之后,最好在针对这个包的测试通过后,我们才发布给下游。
这里隐含着软件测试的分级原理。对于有一定规模的软件项目来说,CI 中的测试往往是多级别的。例如,针对单个模块,有单元测试 UT,模块测试 MT;针对相互联系的多个模块,有集成测试 IT;针对一个完整的包,有冒烟 Smoke 测试,UI 测试,端到端 E2E 测试等。
当然,并不是所有的测试都一定能够成为 CI 的一部分。在加入 CI 之前,测试至少要满足若干准入条件,包括完全自动化,环境易于扩展,执行速度快,外部依赖可控,自身稳定性高等特点。这是一个重要的话题,我们另文讨论。
多级别的软件测试,极大地丰富了 CI 的内涵。有了这些测试的协助,CI 将不仅仅是一个编组包的流水线,更成为一个 7 x 24 小时运转,永不懈怠,严格守护软件质量的 “门神”。
从这个角度来说,包不包含软件测试,或者说包含的软件测试充分不充分,是衡量 CI 有效性的一个重要指标。不包含软件测试,或者软件测试虽然存在但是很不充分的 CI,在我看来是功能残缺,地位有限的。
当然,万事皆有两面性。软件测试加入 CI,既能丰富 CI 的内涵,也给 CI 带来了巨大挑战。这种挑战不仅在于 CI 需要实现许多用于软件测试的任务和脚本,还在于更多方面。
为了应对软件测试海量执行次数的需求,CI 需要准备和维护足够多的测试资源。
为了提升测试执行效率,CI 需要研究和引入并行测试,选择性测试,动态优先级测试等有难度的技术。
为了应对回归测试中广泛存在的测试不稳定性或 test flakiness(原谅我迄今为止没有找到很好的中文翻译),CI 需要对大量测试数据进行持续监控和统计分析,并做好偶现问题跟踪解决工作。
一言以蔽之,测试进入 CI,意味着 CI 需要投入更多资源,并且日常的大部分工作将围绕软件测试和质量保障展开。
如何衡量针对 CI 的这份投入是否值得?需要具体情况具体分析。一般来说,如果加入 CI 的测试的有效性比较好,能够发现软件问题,那么可以认为投入产出比还不错。毕竟在 CI 阶段,软件问题的解决成本还比较低。如果这些问题遗漏到下游,造成的损失和解决的成本不在一个数量级。
啰啰嗦嗦说了这些,其实就是想说明 CI 与软件测试存在紧密关系。抛开测试策略来单纯讨论 CI 策略,往往会失去重心。 通过与多个级别的测试结合在一起,CI 才能如虎添翼,更好完成在软件开发全过程中保障软件质量的重任。
我是肖哥 shelwin,一个高质量软件工程实践者和推动者。欢迎添加我的个人公众号测试不将就或关注同名博客,谢谢,获得更多自动化测试, 持续集成, 测试开发,软件工程实践, Python 编程等领域原创文章。