前言
测试框架的设计需要适合被测试系统,要依据当前测试问题和系统后续的发展合理设计,避免过度设计导致维护成本徒增。同时被测系统也要依据测试方法提供适当的便利,以提高被测系统的可测性。
本文讲述的是基于动态代理的接口测试设计,在思考如何测试之前需要先了解下被测系统。
简单介绍两个被测系统
一、交易订单系统
交易订单系统基于状态机实现,包括正向流程状态机、逆向流程状态机,下图为正向流程其中的一条链路:
- 订单经由特定条件触发,通过执行某个动作由当前状态转移到下一状态。动作执行前由前置校验逻辑判断当前请求是否可执行,当条件为真才会触发后续操作。
- 订单操作以订单上下文为纽带,订单上下文持有订单当前状态及所有信息,订单操作将上下文由A状态变更至B状态并最终落库,同时处理一些UI渲染、消息发送等事物。
二、支付中心账户系统
支付中心账户系统中不存在状态变更,但所有的操作基本都是对账户余额的操作。所以账户系统可以理解为以账户上下文为纽带对账户金额进行操作的系统,如充值操作使账户可用余额增加,提现操作使账户可用余额减少。
上面两个系统有一个共性,被测系统都可以抽象为由某个特定条件下触发某个操作引起某个主体发生特定变化的系统,如订单操作引起订单状态变化,对账户的操作引起账户金额变更。
如何自动化测试类似系统呢?
我们早期接口用例实现方式:
早期我们编写了很多校验方法用于不同属性的校验,如校验订单状态的方法、校验订单按钮的方法、校验订单服务窗内容的方法。在每个订单操作后依次调用该操作对应的校验方法并传入期望值。早期单条用例可读性可接受,从用例即可知道当前做了什么操作,引起了什么变化,期望值是什么。但也存在一些问题,如:
- 同样操作会出现在不同用例中,如不同种类的订单需要调用的校验方法基本相同,导致在不同的用例中重复调用相同校验方法,冗余且容易漏掉。
- 当交易系统更新后,需要到各个用例中更新或替换这些校验方法。当系统复杂到一个操作下需要调用五个左右的校验方法且每个操作还会定制一些个性校验,用例编写和维护会十分痛苦。
那么如何解决这些问题呢?仅仅解决眼前的问题就能满足吗?
我们先看下订单系统的一个特点:一组明确的条件可以定义当前订单所处的状态、UI展示等信息。
- 如买家在已发货状态下操作确认收货,上述条件可以确定订单当前处于已收货状态、订单详情展示为已收货状态下的UI。
- 如订单处于发货状态且买家N天未操作订单导致离线触发,上述条件也可以确定订单同样变为已收货状态。
账户系统如何描述呢:操作充值,导致了余额增加,且条件和结果相对于订单要更加简单。
基于上面的描述,如何改进测试系统呢?似乎只需要拦截操作并构建操作前后的上下文,校验模块通过上下文获取需要的条件匹配当前被测主体的状态、UI进行校验即可。如何拦截?这里就用到了拦截器,依赖JDK中的动态代理实现。
在介绍改进后测试系统结构前先介绍下动态代理
上图是动态代理的UML图,代理类与真正的实现类都继承了抽象的主题类,这样代理类和实际类有相同的方法,可以保证对上层调用方使用的透明性。
写法:
编写一个委托类的接口,即Subject接口
实现一个真正的委托类,即RealSubject类
创建一个动态代理类,实现InvocationHandler接口,并重写invoke方法
那么改进后的用例为:
上图为单个上下文的用例写法,实际测试时往往有多个被测主体,就会有多个上下文维护,多个校验分发的switch。
- 以订单系统为例,订单操作后根据请求参数和返回信息更新订单上下文,动态代理拦截上下文的更新触发校验。将分散各处的校验方法集成为校验模块。通过分发器分发到不同的校验方法中,校验方法通过上下文匹配到当前订单的状态、UI等信息与被测系统返回的订单信息做校验。校验模块又分为各校验子模块,可通过开关控制是否开启。
- 以账户系统为例,账户创建时初始化账户上下文,操作账户更新上下文并触发校验,在校验模块使用用例中维护的上下文信息与真实系统中信息做校验。
方案的优缺点如下:
优点:
- 校验对用例不可见,用例编写者设计好校验模块后只需要罗列操作步骤即可。
- 更高的集成度,降低用例维护成本,系统迭代只需要维护对应校验模块即可。
- 可扩展性高。扩展分为两个维度,业务扩展性和框架能力扩展性。业务扩展性 如:在订单体系中引入一个新的变量,可以通过接入新的上下文快速将新的业务和校验应用于已有用例。接入商品上下文和商品校验子模块用于校验交易过程中商品状态、库存等变化。
缺点:
- 编写用例增加了设计成本,提高了门槛。大需求或系统新增的扩展能力,在用例编写时需要设计上下文、校验子模块,还需要考虑用例的业务可扩展性。
- 代码维护同样有门槛,需要了解框架、调用链和被测业务。
动态代理在交易测试框架中应用
目前接口测试框架中有两个动态代理
- 用于拦截SCF请求的SCF Proxy,可以对请求参数和返回信息进行收集,用于新老环境diff,收集接口响应时间,上报测试环境性能。
- 校验代理,在接口操作执行后通过更新不同的上下文 如订单、商品、红包等触发对应的校验。
动态代理整体的使用理念是拦截对目标类的操作并在目标类的基础上增加切面逻辑,在切面上添加扩展子系统。这就是上面没有展开说的框架能力扩展性。
总结
最后总结一下,设计模式只是抛转,玉是测试系统中的每个模块的设计。后续还有同学介绍如何在当前模式下设计可扩展的清结算校验模块以应对业务高速发展。