在敏捷和精益开发实践的早期阶段,开发团队、运维和 QA 之间出现了非常明显的鸿沟。开发的速度不断提升,但测试过程及方法没有相应改变,整体上降低了软件交付速度,阻碍了企业实现持续交付。传统的测试方法严重依赖于手工流程和频繁更新的 GUI 测试,对于我们的新生命周期时间表来说,这些方法过于繁琐和不足,导致很多问题:

1. 问题滞后:上前线才发现大量 Bug,导致上线严重延期

2. 影响范围变大:一个错误发现的越晚,其影响范围就会不断变大,其他开发人员会使用错误的代码继续开发

3. 修复成本上升:还有时间过长,开发人员对代码熟悉成都会下降,另外因为影响范围可能变大,修复成本会逐渐上升。

4. 二次污染:当你修复一个 Bug 的时候,很可能会带来另一个 Bug,进入修复 Bug,测试,修复 Bug 的不良循环中。

作为事后思考的测试时代正在消亡,持续测试已经成为新的标准。持续测试背后的思想非常简单:就是尽早失败(Fail Fast and Fail Faster),尽最大可能缩短反馈周期。 实施一个系统的改进方法,不仅包括自动化,还包括所有其他的工具和过程,这些工具和过程将有助于在软件发布和生产之前降低风险。

什么是持续测试

持续集成(CI)、连续交付(CD)和连续测试(CT)是许多开发团队采用敏捷策略的三种方法。虽然每个服务都有一个稍微不同的目标,但如果结合起来,它们可以显著地帮助团队实现速度和质量,这是任何开发团队最重要的两个目标。

上线越频繁,Bug 越多怎么办?来了解下 Spring Cloud 契约测试_第1张图片


持续测试是作为软件交付管道的一部分执行自动化测试的过程,以尽快获得与软件发布候选相关的业务风险的反馈。确保每个迭代(Sprint)结束时都有高质量的应用程序。可以说持续测试是敏捷开发和 Devops 的必要条件。

持续测试的5个关键点:

1. 持续测试主要目标是评估业务风险范围。

2. 持续测试建立了一个质量体系,帮助团队在快速开发过程中保护用户体验,避免软件失败。

3. 持续测试要求能够随需创建一个稳定的测试环境用于快速反馈。

4. 连续测试需要无缝集成到软件交付流水线和 DevOps 工具链中。

5. 持续测试应该为交付流水线的每个阶段提供适当的反馈(质量关卡)。

过多关于持续测试的定义请参考:https://www.tricentis.com/what-is-continuous-testing/

如何实现持续测试

为了实现持续测试,自动化率需要超过85%。为了实现这一点,需要做一些改变:

  • 将测试与业务风险相结合,以优化测试执行,实现自动化的质量检验关,并为发布决策提供关键的洞察力。

  • 最小化手工测试,并将其转换为基于会话的探索性测试。

  • 在可行的情况下,将测试工作转移到 API 层。

  • 使用测试数据管理和服务虚拟化,以便能够连续执行实际的端到端测试——没有假阳性和超时。

  • 将功能测试集成到 CI/CD 中,这是交付流水线中无缝集成。

上线越频繁,Bug 越多怎么办?来了解下 Spring Cloud 契约测试_第2张图片


微服务环境下自动化测试难点

微服务的一个标准就是可以独立发布,下图是 Netflix 内部微服务结构,每个团队独立发布自己的微服务。

上线越频繁,Bug 越多怎么办?来了解下 Spring Cloud 契约测试_第3张图片


因此在微服务架构下,你的服务可能由不同的团队提供和维护,在这种情况下,接口的开发,测试和维护可能会带来一些问题

1. 服务端调整架构或接口调整而对消费者不透明,导致接口调用失败。

2. 微服务测试环境搭建困难,存在大量直接依赖和间接依赖,测试时需要准备所有依赖的服务,测试一个服务,你不得不部署其他微服务,也许还有耦合的数据库、消息队列等。

3. 很难进行 Debug,反馈周期变长。

4. 微服务版本及接口独立维护,出现接口不兼容现象,导致微服务升级失败。

上线越频繁,Bug 越多怎么办?来了解下 Spring Cloud 契约测试_第4张图片


微服务测试框架 – 消费驱动契约 CDC

消费者驱动的契约测试(Consumer-Driven Contracts,简称 CDC),是指从消费者业务实现的角度出发,驱动出契约,再基于契约,对提供者验证的一种测试方式。如果你目前使用 SpringCloud 作为微服务基础环境,那么集成 SpringCloud Contracts 也是比较好的选择。

使用 SpringCloud Contracts 之前:

上线越频繁,Bug 越多怎么办?来了解下 Spring Cloud 契约测试_第5张图片


使用 SpringCloud Contracts 之后:

上线越频繁,Bug 越多怎么办?来了解下 Spring Cloud 契约测试_第6张图片


如图所示,我们只需要关注直接依赖,看起来和 Mock 测试相似,它的不同之处在于消费者来驱动测试,从而提高透明性,服务提供方流程简单描述如下:

1. 服务消费方提出需求(契约 Contract)

2. 服务提供方根据契约完成功能开发以及功能单元测试

3. 服务提供方自测发现不满足契约时不能进行升级和发布(Fail Fast and Fail Faster)

4. 服务提供方根据契约自动生成测试桩(STUB),用于消费方进行依赖测试

5. 消费方测试时根据直接依赖情况,下载直接依赖的服务STUB,并启动 STUB 服务模拟测试依赖的服务,不需要下载传递依赖

6. 服务提供方不得单方修改接口服务,需要根据消费方契约的修改而变更,保证服务提供方变更的透明性

7. 服务提供方作为消费方时重复1-6步。

服务端定义契约

这里引入了一个重要的概念,就是契约,Contract。这是什么呢?很简单,就是 Provider 和 Consumer 事先要约定好一个接口的规范,之后双方提供服务接口和消费服务接口都要按照这个契约来。来看一个契约描述,契约采用 Groovy 的 DSL 描述,所以一目了然,就是通过url:/ api / customers来取得一个 Json 格式的客户列表,返回两个客户信息。

上线越频繁,Bug 越多怎么办?来了解下 Spring Cloud 契约测试_第7张图片


根据契约实现服务后,我们根据 Spring Cloud Contract 的一个 Maven 插件(spring-cloud-starter-contract-verifier

),执行 Mvn Install 后会自动生成服务 Jar 包和测试桩 Stub,如下图

上线越频繁,Bug 越多怎么办?来了解下 Spring Cloud 契约测试_第8张图片


消费方消费服务和测试

消费方同时需要依赖一个 Spring Cloud 组件:spring-cloud-starter-contract-stub-runner,通过这个依赖,我们就可以快速启动服务提供方提供的 Stub 来模拟启动一个契约服务了。

上线越频繁,Bug 越多怎么办?来了解下 Spring Cloud 契约测试_第9张图片


其中使用了 Spring Cloud Contract 组件中的注解:

@AutoConfigureStubRunner(ids = {"com.importsource.springcloud:spring-cloud-contract-provider:+:8080"}, workOffline = true)

我们通过@AutoConfigureStubRunner 来自动配置注入一个 StubRunner。Lds 来指定直接依赖的服务,并指定服务端口号。这样我们根本不需要启动真正的服务提供者,而是在本地启动了 Stub 就模拟测试了一次服务调用。

总结

本文首先向你介绍了持续测试的背景概念以及如何来做持续测试, 同时介绍了消费者驱动(CDC)测试的场景,以及消费者驱动契约的一个实现 Spring Cloud Contract,我们通过定义契约进行微服务框架下的持续测试,稳定可靠的加速 CI/CD 持续交付。另外契约测试的工具除了 Spring Cloud Contract 外,还有其他的一些工具可供你选择,比如:Janus,Pact,Pacto 等。

展望未来

很多企业已经经历了相当长的一段时间来进行持续的测试。然而,当我们展望未来时,很明显,即使是持续测试也是不够的。我们正在快速接近一个新时代,持续测试将无法跟上不断缩短的交付周期、技术复杂性增长以及变化的速度。

在一个软件将实时处理难以想象的数据点的时代里,例如,无论是从物联网,还是“自动驾驶”。除了持续测试外,我们还需要“数字测试”(Digital Testing)来实现进一步的加速,并满足未来由物联网、机器人技术和量子计算所驱动的质量需求。人工智能,模仿智能人类行为的机器学习和预测分析,可以帮助我们到达那里。

上线越频繁,Bug 越多怎么办?来了解下 Spring Cloud 契约测试_第10张图片