别再加端到端集成测试了,快换契约测试吧

正如大家所知,最初QA都是手动执行测试用例,开发人员每修改一个版本,QA就要手动测试一遍,随着功能的不断增加,手动测试重复的工作量越来越大。为了解脱QA重复性劳动,提高工作效率,重复执行的测试用例被自动化了。自动化测试让QA的工作前进了一大步。

本文讲的端到端集成测试(简称集成测试)是指系统集成后的自动化测试,是系统或模块真实组装后运行的测试。很多团队用UI端到端来测系统集成后的行为,这类工具很多,比如有Selenium webdriver等。端到端的集成测试反馈与修复的周期比较长、运行速度慢,测试运行不稳定,有时随机失败,维护成本也很高。它不像单元测试,单元测试测具体一个方法或API,定位准确,采用Mock机制,运行速度非常快(毫秒级),又是开发人员在本地执行,反馈修复及时,成本较低。

于是,我们把绝大部分能在单元测试里覆盖的用例都放在单元测试覆盖,只有单元测试测不了的(比如模块或API之间连通性),才会通过端到端的集成测试来覆盖。此时,测试又前进了一大步。

但是,随着业务的不断拓展、产品功能不断增加,系统架构越来越复杂,端对端集成测试的成本越来越高,测试用例也越增越多,集成测试又成了快速验证的阻塞区。在当今持续集成的开发模式中,开发团队会频繁集成,每次集成都会通过流水线(Pipeline)快速验证、准备部署包、进而发布。然而,集成测试的这些问题会严重影响或阻碍产品快速发布。

那么问题来了,怎么解决集成测试的现有问题,让测试再前进一大步?

其实,早在几年前,著名的敏捷和TDD专家JB Rainsberger就提到了。

“集成测试是个骗局”,正确的是应该用一种契约或协议测试来测试集成后的系统行为!

JB Rainsberger认为你写的2-5%的集成测试和单元测试有重复,或者和其它地方的集成测试存在重复,而且当集成测试失败时,你也不知道发生了什么,不能及时准确定位问题。

JB Rainsberger认为应该让契约测试来替代集成测试,那么,什么是契约测试?它是否能解决集成测试的这些问题?

契约测试

契约测试是验证服务的Provider是否按照期望的方式与服务的Consumer进行交互,简单的说是Consumer与Provider两者之间的集成。

而Contract即合同、契约,就是Provider与Consumer的交互方式。

契约测试通常是基于Consumer驱动的(Consumer Driven Contracts,基于Consumer驱动的契约测试工具有PACT)。基于Consumer驱动的契约测试分两个阶段:

  1. Consumer生成契约,开发者在Consumer端写测试时Mock掉Provider,运行测试生成契约文件;
  2. Provider验证契约,开发者拿契约文件直接在Provider端运行测试进行验证。

第一阶段:Consumer生成契约

第二阶段:Provider验证契约

如何用PACT编写契约测试,这里就不赘述了,实例详情请参见PACT an example

集成测试的特点

  1. 真实安装后测试,测试更接近真实使用情况;
  2. 可见性强,容易理解;(比如:看一遍运行关键业务的集成测试,业务人员或客户会觉得很放心。也可以替代验收测试)
  3. 模块真实调用,测试运行慢,秒级别或分钟级别,反馈与修复的周期慢,成本高;
  4. 问题定位难,多个子模块组合安装后的测试,很难定位是哪个模块出的问题;
  5. 真实的安装或环境搭建,不稳定,容易导致测试随机失败;
  6. 沟通成本高,需要不同模块团队间的协调工作;
  7. 与底层测试或集成测试会有重复,集成测试中有的路径已经被单元测试覆盖。

契约测试的特点

  1. 开发人编写,采用Mock机制,开发本地就可以运行,没有真实调用,运行快,毫秒级修复反馈周期短;
  2. Provider与Consumer两两之间的验证,容易定位问题,而且与底层测试或其它契约之间没有重复;
  3. 不需要部署真实的集成环境,稳定且成功率高;
  4. 沟通成本低。(比如一个Consumer端的加入导致服务端API修改,服务端开发人员不必跑去找所有其它Consumer端开发人员沟通确认是否会被影响,直接运行契约测试就能知道结果。)

由此可见,开篇谈到的端到端集成测试运行慢、不稳定、修复反馈周期长等等问题,都能通过契约测试得到解决或改进。

举例说明

假如某社交聊天产品(简称TWChat)的架构是这样的:服务端、客户端、邮件通知服务三部分组成。

架构图

通常的测试策略:底层绝大部分的单元测试+少量上层端到端集成测试。

用TWChat注册场景来举例说明吧。注册一个帐号的工作流是:客户端把注册帐号信息提交给服务端,服务端处理帐号时,会去调用邮件通知服务发通知,并完成注册。

底层单元测试用例

单元测试

  1. 客户端的单元测试:验证注册表各个Field的各种输入组合、以及检验正确性等;(比如:边界值、空、中英数各类组合、合法与非法输入等)
  2. 服务端的单元测试:验证注册数据表的各种输入组合可以成功存放于服务端帐号DB表中,且不合法的、重复等会有相应的错误码;
  3. 邮箱通知服务端的单元测试:输入合法的各类不同的邮箱确,保证能正常发出通知邮件并返回正确码,输入不合法的邮箱或空邮箱确保有相应的错误码。

上层端到端集成测试用例

集成测试

一条注册连通性的Happy path测试用例, 输入所有必填项提交,验证注册成功,收到成功通知邮件。

以上的集成测试,必填项输入其实是与单元测试重复,邮件通知发送功能与单元测试也有重复;再者,这条集成测试跑失败,我们并不能定位是客户端的问题、服务端问题、还是通知服务的问题。加上集成测试是把所有子模块(服务端、客户端、通知微服务)真实产品安装包部署以后才能运行的测试,反馈、修改周期长,不稳定容易随机失败等等。

集成测试换成契约测试用例

契约测试

  1. TWChat客户端Consumer与TWChat服务端Provider加一条契约测试,确保TWChat服务端按期望提供给客户端接口(参见PACT an example)。
  2. TWChat服务端Consumer与邮件通知服务Provider之间加一条契约测试,确保邮件通知服务按照预期与TWChat服务端交互(参见PACT an example)。

契约测试与单元测试以及其它测试之间没有重复,它是单纯验证Provider与Consumer之间按预期的方式交互,定位准确;不需要部署真实的系统环境、Mock机制、没有真实API调用,运行非常快、反馈及时、修复周期短、成本低,在这种情况下,自动化测试流水线运行更快了,产品流水线出产品安装包也更快。因此,显然契约测试才是真正对的选择。

微服务架构下契约测试的重要性

例如,随着TWChat业务的扩大,TWChat钱包,TWChat安卓端,TWChat iOS端,以及其它的服务方与Consumer方接入TWChat服务器。

当其中TWChat安卓端修改后,如果还按照之前的集成测试方式,就得把服务端与所有的客户端真实的集成到一起测试,确保都没有被影响才能生成产品安装包并发布,这里的集成测试成了流水线(pipeline)的一个聚集地,也成为了产品发布的阻塞区。

集成测试流水线

假如,换成契约测试,我们把契约测试放在各自的流水线(pipeline)上,每次代码提交触发相应产品流水线上的契约测试,当TWChat安卓客户端Consumer API修改,在安卓客户端的流水线(pipeline)上运行安卓客户端为Consumer与服务端为Provider的契约测试,测试通过,生成产品安装包;如果契约测试失败,服务端需要相应修改,则本次TWChat安卓端的安装包需要在TWChat服务端修改后,方可生成安卓客户端的产品安装包。

契约测试解耦后

由此可见,并不是每一次TWChat安卓端的修改都要全部Consumer端与服务端集成后验证才出包,而是各自可以独立出包,产品解耦,大大节省时间,提高出包频率。

并非所有端到端集成测试都适合换成契约测试

契约测试相比端到端集成测试有很多优势,但并不是所有场景都适合契约而非集成测试。

比如:

  1. 契约测试无法做安全或性能测试等。
  2. 契约测试采用Mock机制,所以没有集成测试更接近真实环境,也不能给业务人员做验收,可视性差。
  3. 契约测试基于不同的服务使用的协议不同,验证契约的复杂度会不同,复杂度过高时,需要权衡是否有必要加契约测试。

所以,把端到端集成测试要换成契约测试也不是绝对的,视情况而定。

总的来说,当你追加端到端集成测试的时候,如非特殊,快换契约测试吧。


更多精彩洞见,请关注微信公众号:思特沃克

Share

QA Review UT的那些事儿

一个风和日丽的下午,眉清目秀的Dev小乒挺了一下腰,喊了BA、UX还有QA小乓一同SignOff,十几分钟后,场景验收完毕,BA和UX退位,只有QA小乓还定坐在那里,斩钉截铁地说了句:“看下UT吧”,故事也就从这里开始了…

“嗯?还要看UT?” Dev小乒一脸突然。

“嗯,看下吧!” QA小乓谈定的说。

“好吧……”Dev小乒边回话边打开Git翻找,嘴里好像还在嘀咕着什么。

“这部分测了保险单的消息提醒功能” Dev小乒随手打开一个测试文件。

“那理财单的消息提醒测了没?” QA小乓追问道。

“没测,不用测,实现都一样!” Dev小乒顺口答道。

“只测保险单,那理财单的挂了怎么办,它们是不同的业务,难道,还得手动一遍遍测试?” QA小乓皱了皱眉看了一眼Dev小乒。

“不用,相信我,我们是TDD,测试驱动开发,再加也驱动不出新代码,再说实现都一样,加了也是重复代码。”

说完这段话后,Dev小乒身子往后一靠、椅子向后一滑,身体转向QA小乓,语重心长地说:“小乓呀!我觉得吧,你不用看UT,对你没啥用,再说都跟实现相关的,也很难给你讲清楚……”

此处的QA小乓仿佛看到了无数只黑乌鸦从眼前飞过……

这就是我们项目在尝试“QA Review UT”实践后出现的一个真实场景,从中可以看到这个新的实践所面临的挑战,那问题来了?对呀,为什么QA要Review UT呢?

为什么要Review UT

1、先从UT的重要性说起

根据测试金字塔,UT处于测试金字塔的最底端。其特点是更贴近于代码,运行速度快,成本相对较低,对问题的反馈也更准确。所以通常情况下,绝大部分的业务代码都会在UT层被覆盖,也就意味着有近70%或更高比例的业务逻辑会由UT来保护。

1-test-pyramid

2、再从”QA对UT”不可见的弊端谈谈

作为把控质量的QA,对于这类在测试策略中占比最大的单元测试(UT)却通常是不可见的。因为UT通常都是由开发人员编写,与实现紧密相关,又纷繁复杂,所以QA通常都不知道在UT中到底测了哪些、没测哪些。这样一来,只能通过手动一遍遍测试或编写更加昂贵的UI回归测试予以保障,不仅耗费了很多精力,而且还可能会与UT测试有很大的重复。

但如果加入了UT Review,使UT对QA可见,可以让QA更好的了解产品的质量情况,制定更加有效的测试策略,就可以在增加整个团队对于产品质量信心的同时,使工作更加高效合理,测试策略更加精简有效。

由于QA对于业务的理解也比较深入,有时还能在Review中发现漏掉的测试场景,在更早期识别风险,避免更大问题的产生。

由此可见,Review UT很必要,可是往往好事多磨,就像一开始的故事,QA小乓就遇到了很多困难。

Review UT过程中的那些困难

2-Review-UT

1、开发人员内心排斥QA Review UT

回想一下一开始的场景,Dev小乒对QA小乓要Review UT表现得有些突然,包括Dev小乒最后语重心长的那句话。从Dev的角度,UT是与实现相关的,与技术架构、工具选型、甚至是底层设计相关,QA又不懂。另外,QA要Review他们写的UT,可能会让Dev觉得这是对他们的不信任。再者,SignOff加入Review UT后,时间变的更长,成本也会相应变高。所以,在刚开始推行Review UT的过程中,会出现双方合作不畅的情况。

【解决方案】:在Review UT这件事上团队内必须先达成共识,通过沟通使团队明白QA Review UT不是因为对Dev不信任,也不是为了挑刺儿;而是为了挖掘前文中为什么要Review UT章节中阐述的QA Review UT能给项目带来的好处。另外,作为QA也需要了解些代码方面的知识,比如:系统架构、Dev用的TDD等,以便于更好的理解UT,最终通过双方的努力,不断的降低Review UT带来的成本,实现利益的最大化。

2、测试结构乱,看不懂

刚开始Review UT就发现,UT结构零散,经常需要在不同代码库、文件、及Commit中跳来跳去,全程Review下来经常会感觉一片混乱,也搞不清哪些测了哪些没测。甚至还会使得双方怨念四起、互相嫌弃,QA嫌弃Dev写的乱、又讲不明白,Dev嫌弃QA看不懂、还嘚吧嘚!

【解决方案】:首先Review UT对QA提出了更高的要求,要对代码架构提前有个了解。而Dev在写UT时,也要尽可能写的结构清淅易读、命名准确。在每张卡SignOff时,开发人员可以先简短介绍清楚UT的结构(也可以简单画一画),再介绍哪部分Test Case放在了哪里,然后去对应的结构里看相应的测试 。这样整个过程才会更加的顺畅,也才能真正的起到预期的作用。

3、根据现有实现,不需要再加

开发人员有时会说:“根据现有实现不需要加,这个业务和另一个业务实现代码是一样的。再比如,这方法的实现是用数组,你提到的两种业务场景在这里就是数组里的1和2,处理上没区别”。

【解决方案】:测试验证的应该是业务价值,而不是具体的实现方式。而在业务价值不变的前提下,实现代码却可能会发生变化,比如:代码可能被重构。所以测试也不该根据代码实现来设计CASE,更应该从业务价值来设计。当然实际情况中会有一些特殊情况,但总体原则不变,即我们应该更多的通过测试来保障我们交付价值而不是具体实现。

收获

经过一段时间的实践QA Review UT,我们发现在不同程度上,都有了很大的收获。

1、及时发现了UT上的一些问题并改进

Review UT以后,SignOff时发现了不少漏掉的测试场景,让一些Defect提前被识别出来,避免了更大的损失和风险。

2、QA越来越熟悉Code&Test结构

要想Review好UT,QA要大致了解系统的技术架构,而且每次Review UT前,开发人员还会给介绍或画一下测试结构,时间久了,对于代码和测试的结构就会越来越熟悉,从而让整个Review过程变得越来越有效率。

3、QA和开发人员更近了

在Review UT的过程中,QA和开发人员之间更多的交流,介绍测试结构时,会提到实现架构与实现等,渐渐QA对DEV的术语也懂的多了,也更了解了开发人员的测试思维,沟通合作起来更顺了,Review UT过程中遇到双方争异也更易解决了。

4、开发人员测试sense提高

QA更多地从业务角度以及从测试专业角度思考问题、交流,在QA潜移默化地影响下,开发人员对于测试和质量的Sense也有了提升,UT的质量也有了明显的提升。

5、团队对质量更有信心

QA参与和开发人员一同严格把控了重要的UT环节,也清楚了哪些业务价值已经在UT中被覆盖,所以在设计更高层次的测试时就会更加有针对性,减少了重复测试带来的浪费,同时团队对产品质量也更有信心了。

6、QA做更多有价值的事儿

被UT覆盖的业务价值对于QA来讲不需要做完整的细枝末节的手动回归测试了,只需要做一些针对性的探索性测试,这样QA就有了更多的时间做更有价值的事情。比如:更早参与需求分析、Review Story,写Regression测试,实施內建安全&性能, 以及生产环境下QA的事儿等等!

Share