实用契约测试:案例研究

6天前

领略“后端+前端+推送服务器”的模式设定如何处理WebSocket契约测试。

本文由公众号EAWorld翻译发表,转载需注明出处。


作者:KRISTOPHER SANDOVAL
译者:白小白 
原题:Functional Contract Testing: A Case Study
原文:http://t.cn/EIkf6pf

引言:

契约,或坊间常说的契约测试,已经越来越多的成为现代WebSocket API实现的一个组成部分。契约测试所遵循的理念是:内容的变更可以通过契约的形态呈现,并且契约可控亦可测。这一理念借助“后端+前端+推送服务器”的模式设定,正成为很多系统的关键。

在本文中,我们就将领略这样的设定如何处理WebSocket契约测试。我们将把Billie这一金融平台作为研究对象,Artem Demchenkov是这一平台的联合创始人,并曾在2018年的Nordic APIS平台峰会上发表过关于这一内容主题演讲。我们将探究其实现的独创性,并了解何以这一实现可以处理企业级生产环境中复杂的测试活动。

Artem Demchenkov的演讲内容的解PPT地址是(需要科学上网):http://t.cn/EIkMcsa

目录:

一、契约是什么?
二、测试问题
三、Pact契约测试
四、这一测试过程的实现原理是什么?
五、结论

一、契约是什么?


在Billie的通信模型中,契约主要体现为两个微服务组件之间的一个或一系列既定进程。契约阐释了每个微服务的功能、通信方式以及通信的交互格式。换句话说,API契约测试和普通契约测试并没有什么不同,与契约的背离,说明通信过程出现了问题,而这一问题又预示着更大范围的失败。

在本案例中,理解契约的本质至关重要。为了了解Billie处理契约测试的过程,我们首先需要了解其背后的通信模型。在Billie的通信模型中有三种基本的组件:前端 、后端和推送服务器。用户数据将被优先从后端推送到前端 ,但并不是使用传统的HTTP请求模型,那是怎么办到的呢?

我们可以将这一通信模型拆分为五个基本步骤:

第一步,WebSocket连接:从前端(用户端)到推送服务器之间建立连接; 
第二步,WebSocket授权:用户数据,尤其是用户ID和用户的API秘钥,将由前端发送到推送服务器,从而完成用户认证。直到客户端关闭连接或者发生连接失败之前,这些用户数据都将一直保存在内存中。
第三步,数据变更:当发生了内部的数据变更的时候,变更数据将推送给后端; 
第四步,API端点推送:变更数据由后端向前传导到推送服务器 。
第五步,WebSocket推送:推送服务器完成对前端的数据更新。



图片来自Artem的PPT

契约的本质决定了通信的关键元素,并且对契约偏差的侦测将有助于在问题出现时实现对问题的解析和定位。

二、测试问题

这种方式的问题在于,随之而来的重要的单点失败。推送服务器将成为用户前端和服务提供后端之间的重要通信控制节点,这将导致不特定数量的单元之间的的通信故障。从最基本的通信故障到灾难性的推送服务器失败。当推送服务器失败并且无法传输数据的时候会发生什么?当推送服务器无法接收前端连接会发生什么?后端甚至都不知道故障已经发生,怎么办?

因此,Billie意识到必须以一种有效且高效的方式对这一通信过程进行自动化的测试。适当且自动化的测试可以确保系统处在正常工作的状态,且总体上的通信模式健康且符合预期。Billie进行了大量的测试:


图片来自Artem的PPT

本文将集中精力在契约测试方法这一内容上,Billie称之为“Pact Contract Testing”。

三、Pact契约测试

在这一测试方法中,有三个组件,对应通信过程的三个组成单元。消费者,对应前端;Mock服务器,对应推送服务器;服务提供者,对应后端。



图片来自Artem的PPT

我们可以预设一些基本的交互过程用以衡量测试偏差:

服务消费者应该发送某个特定请求,并期待返回特定格式的响应作为回应; 
服务提供者期待接收某个特定请求,并且以其严格的响应格式和通信模型作为回应; 
Mock服务器坐镇中央,感知双方所在的领域,扮演协调人的角色,管理着所有的交互过程。 上述预设形成了一个契约模型,我们实际上要测试的,就是相对于这一模型的偏差是否发生。

测试本身借鉴了Erlang的理念,尤其是在对测试进程的处理上。在Erlang中,所有事物都是进程,进程要么完成既定使命,要么失败。这种非此即彼的设计理念在Billie的用例中十分适用,因为交互过程主要依赖通信过程的出现与否:有通信则有交互,没有通信,则没有交互。

契约测试体系大体上是对前文所述的5步骤通信模型的映射,只不过用一系列的测试进程代替了实际的通信流量,用以测试通信路径上的节点是否正常工作: 

测试进程生成了消费者和推送服务器之间的流量(模拟通信模型中的第一步),此时,测试进程主要作为后端的模拟来发挥作用。当测试进程从客户端请求用户数据时,这一数据将被推送到Mock服务器,以进行认证操作。测试进程再次向消费者进程发起交互过程,来模拟常规的通信过程中后端数据被更新时发生的数据推送过程。

服务提供者将首先生成测试进程,并启动Mock服务器来进行测试。然后,消费者进程启动,用户端将收到一个请求来将其认证数据提供给Mock服务器。有了这个数据之后,Mock服务器生成验证,模拟常规通信过程中的第二个步骤。

当测试结束 ,代码将进行自我清理。消费者进程结束,推送服务器结束 ,测试过程关闭WebSocket连接并返回结果:测试失败或测试通过。

这一测试方法主要基于进程分离的理念,但也可以扩展为其他的解决方案。在这一演讲中,Artem提供了一个PHP的示例实现,在示例中,唯一的不同是消费者不再是一个进程,而是由测试进程初始化的对象。

四、这一测试过程的实现原理是什么?

需要指出的是,Billie模型中的一些组件的设定使得这种类型的测试得以实现,尤其是通信模型中的每个组件之间的相互关系:

1、后端是数据的提供者;
2、前端是数据的消费者; 
3、推送服务器作为通信单元存在;
4、各要素之间仅存在两种交互过程:连接与授权以及前后端推送。

这些关系形成了一种可以测试的重度收敛的通信模式。更明确的是,连接和授权之间的交互,以及从后端到前端的基本推送方式,无不依赖于一个“非成功即失败”的契约化的通信系统。在这样的系统中少有歧义。

对于Erlang理念的借鉴也是一个重要的使能因素。当所有的事物都是进程,且每个进程者可以被唯一的标识,针对交互过程和独立的进程的测试变得更为简单。

五、结论

表面上看,契约测试很简单,但Billie测试其关系的实际过程是十分有趣的,并在体现了对于这类关系的测试难度。

彼此分离的系统之间的严格的契约使得通信更加可靠,但同时也使得通信过程的测试变得更加困难。当进程和服务器被移除时尤其如此。因为对关系、认证、内容和通信路径合法性的验证,依赖于通信路径的完整性。

对于这一进程,你的想法如何?是否有更有效且高效的方法处理这类测试?请在评论区给出你的答案。


关于EAWorld:微服务,DevOps,数据治理,移动架构原创技术分享,长按二维码关注

COMMENTS

需要 后方可回复
如果没有账号可以 一个帐号。