引言

本文是《持续交付》一书学习总结的第二篇。主要内容涉及持续集成以及持续交付中的测试实践。

《持续交付》(二)持续集成与测试

持续集成(一)

关于持续集成(CI),我想程序员朋友都很熟悉了,现在很少有项目不采用持续集成了。我们来看一下其中的原则和实践。

持续集成要达到的目的是,每当有开发者提交了改动,整个应用程序需要重新构建,并运行足够广泛的测试来保证质量,以使得应用程序始终处于能够正常工作的状态。一个典型的CI过程如图所示。

《持续交付》(二)持续集成与测试

要想达到目标,需要几个前提条件。首先就是代码的版本控制,这一点已经不止一次被作者提到。版本控制的好处在前面已经分享过,没有版本控制,持续集成就很难顺利进行。然后就是自动化构建,这里的自动化指的是利用专业CI工具,将持续集成的过程自动化,而不要试图自己实现。常见的持续集成工具有开源的Jenkins,和付费的TeamCity等。最后就是整个团队要CI的意识,比如代码要及时提交,CI过程发现错误,要及时修正,不能把CI的过程当作摆设。

下面分享一些持续集成实践的关键点。

  1. 有规律地提交代码(永远不嫌烦)。
  2. 创建广泛的自动化测试。持续集成阶段的测试,通常是指单元测试、集成测试以及验收测试(Acceptance Test)。在具体实践中,前两种测试更常见,而验收测试通常关注产品的功能和非功能特性是否符合预期,这就需要部署真实的类生产环境出来。我们一般意义上CI就是到构建结束,产生用于部署的二进制文件为止(称为提交阶段,commit stage)。这里的持续集成则是广义的,包含了部署以及后续的验收测试。因此,从广义上讲,每一次代码的提交,都是经过持续集成过程验收的。
  3. 构建和测试过程时间不要过长。构建和单元测试时间太长会产生一些后果,比如开发者会没有耐心等待整个build运行结束就继续提交代码,而最后build失败了,也不好确认是哪次改动导致的。一个理想化的标准时间长度是10分钟,当然这个具体时间一定是跟项目相关的。对于单元测试,目前主流的测试工具都能统计每个case的执行时间,这样就可以定位哪些case耗时,从而进行优化。

一个好的实践是CI过程这些任务都可以在开发者自己的开发环境上运行,开发者首先要保证的是代码在自己的环境上build通过,没有单元测试失败,并且可以部署本地环境,进行简单测试。在实际工作中,这可能比较理想化,很多系统和软件在本机部署时很困难的。但一旦有可能,很多问题就可以在代码提交前被避免。

后面我们会看到更多关于持续集成的实践。

持续集成(二)

下面我们继续来看持续集成的一些最佳实践。以下是《持续交付》这本书推荐的一些持续集成阶段的实践经验:

  1. Build失败时不要继续提交代码。这一点很直观,build失败时的主要任务就是调查原因,修掉问题,继续提交代码可能让问题的调查更复杂。
  2. 提交代码前在本地运行提交测试。提交测试通常指单元测试和集成测试。开发者有时会忘记执行这个测试,因为他们在调试环境中已经确认自己的代码生效,并且没有问题。但他们忽略的是,这些代码改动可能导致其他部分功能不工作,即引入回归问题(regression bug)。这时提交测试能帮助开发者发现这类问题。
  3. 不解决Build失败的问题前不要回家。这里当然不是提倡加班解决问题,而是如何对待Build失败的情况。在团队合作开发时,不同开发者的代码可能会提交到同一个特性分支上。如果你的改动导致build失败,而你没有解决就下班回家了,那么第二天你的同事看到失败的日志,就可能自己试图解决,而由于他不一定熟悉你的代码,因此可能解决不了反而引入新问题。
  4. 随时准备回退到上一个版本。这一条说的是,如果遇到build失败,不能花过多的时间解决问题。长时间没有解决问题,就应该回退代码到上一次成功的版本。
  5. 不要把失败的测试case注释掉。这一点不多说,如果测试case失败,很可能就是代码有问题,注释掉case就是自欺欺人。
  6. 采用测试驱动开发方法。为了实现持续集成的快速反馈的优点,我们要提高测试的覆盖率。而提高测试覆盖率最好的方法就是采用测试驱动的开发,case不写好,不开始写功能代码。当然凡事不绝对,每个团队可以依据项目的特点决策。

注意上面提到的提交代码,都是提交到一个公共的开发分支,这个分支后续会用来部署环境,因此必须是稳定的。在实践中,主分支通常是main或master分支,开发分支通常是develop分支,开发者自己建的分支叫feature分支。持续集成通常在feature分支上也会执行,但不会影响其他开发者。当在开发分支和主分支上运行时,就一定要保证build成功了。

下面还有一些情况需要直接让持续过程失败,以防止影响扩大,如测试过程执行过慢,或者代码中有警告和代码风格的问题,当然这些只是可选的,每个项目组可以根据自己的习惯来执行。
持续集成就学习到这里。下面我们看看持续交付中的测试阶段怎么来进行。

持续交付中的测试(一)

在持续交付的过程中,测试是很重要的一个环节。为了提高反馈效率,尽快发现问题,测试通常以自动化的方式实现。当然,必要的手动测试也不可或缺。前面在谈到持续集成时,我们涉及了单元测试、集成测试和验收测试,这些类型的测试都是从不同阶段、不同角度对软件质量的评价。用书中的话说,一个广泛的自动化测试集,就是一套最新的文档,该文档不仅指定了软件系统应该如何运行,还揭示了软件系统实际上是如何运行的

在看具体的测试实践之前,我们先来看看测试的分类。下面是一个有趣的分类方法,它将测试分为四个维度,如下图。

《持续交付》(二)持续集成与测试

上面这个维度表示面向商业价值(business facing)的测试方向,左边的维度表示支持开发过程(support programming)的测试方向,二者结合就是左上角的这一块。它主要包括功能性的验收测试。

这部分的验收测试通常是需要自动化的,即需要开发者进行编程的,自动化能提高反馈的速度,这对持续交付至关重要。验收的目的是检查软件产品在功能上是否达到期望的需求,这就要求在设计测试case时,要覆盖一些关键的功能路径。我们不能奢望通过自动化解决所有测试,但一些重要的功能还是可以覆盖的。另外,自动化的功能测试case集也可以作为回归测试的测试集。

图中下方的维度表示面向技术(technology facing)的测试,右侧则代表评价产品(critique project)的测试,二者的结合就是右下角的非功能性验收测试。非功能性的测试关注软件产品的容量(capacity)、可用性、安全性等,这些指标虽然是从技术角度考量,但也是评价产品的标准。这类测试可以是手动的也可以是自动的。比如我们可以通过静态代码扫描的方式发现源代码中的安全性问题。而对于可用性,就可能需要手动进行一些操作来测试软件产品的行为。

类似地,图中左下角的模块关注面向技术的且支持开发的测试,通常是指单元测试和集成测试,这一点我们在前面一节持续集成里面说过了。右上角的模块关注的是面向商业价值且用于评价产品的测试,这通常指的是用户界面上的易用性和探索性的测试,说白了就是用户在界面上实际操作,看看产品好不好用,可以不遵循测试的步骤,完全以客户的角度来试用产品。我们常说的beta测试就是这个类型,它也常用于金丝雀发布(canary release)等发布流程中。

下面我们来看看一些具体的测试实践。

持续交付中的测试(二)

对于一个新项目,我们最好在一开始就编写自动化的验收测试case,这些case需要定义好明确的验收标准(acceptance criteria)。之所以在一开始就写测试,是因为随着项目的进行,说服开发者去参与测试用例的编写就比较困难。另外,验收测试的标准是一定要能够证明,软件产品把期望的商业价值交付给用户。

对于进行到一半的项目,最好的方法是,针对最重要、对用户价值最高的部分,设计自动化验收测试。这样既减轻了流程上的开销,又能够在一定程度上保证软件产品的质量。使用自动化测试覆盖重要功能,也能减轻手动测试的压力,后者就不需要覆盖所有测试点。这里面还有一个实际问题就是,随着项目的进行,产品的特性不停地变化,测试case需要频繁更新。这时,与其花费大量精力去维护测试用例,不如有选择地跳过一些不那么关键的case。具体情况,每个研发团队可以根据自己项目的实际情况决定。

对于老系统,开发者可能会有一个疑问:已经上线运行很久的产品,为什么还要编写测试case呢?其实,这些自动化测试case可以作为回归测试集,保证产品的稳定运行。类似地,这些测试集只需要针对系统中的关键功能就行了,不用耗费过多精力。

下面来说说集成测试。前面在持续集成部分已经涉及过集成测试,那里的测试跟单元测试一起执行,作为build阶段的一部分。集成的对象通常是某个模块的内部组件。而广义的集成测试,也包括模块之间的互相集成和依赖的测试。现在很多企业的云计算产品采用微服务架构,每个微服务内部的组件之间需要做集成测试,而不同微服务之前的调用也需要测试。服务之间的这种集成也可以作为冒烟测试集,用于验证部署过程。

集成测试中可能遇到的问题是,依赖的外部系统或服务可能是生产环境。为了防止误操作影响生产环境,可以在外部系统前加防火墙。或者通过配置,将访问路径指向模拟的生产环境。另一个需要注意的问题是,在设计调用外部模块的测试case时,要采用黑盒测试的方式,考虑到外部模块所有可能的响应,并作出相应的处理。

最后一个注意点就是维护好bug的列表,用于追踪和记录。bug列表要有明确的责任划分,每个bug要定义验收标准,并对所有人可见。bug列表是驱动流程运转的重要工具,目前主流的业界产品有JIRA和OneBug等。

关于持续交付中的测试的基本概念和实践就介绍到这里。下面我们来看部署流水线(deployment pipeline)的概念。

相关文章: