You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

14 KiB

21设计实战 一个金融交易业务的自动化测试设计

你好,我是柳胜。

一提到金融业,可能你第一时间想到的就是钱。没错,存款、转账都是普通人最常接触的金融业务。不过在专业金融人士那里,钱的形态和流动方式也更加复杂,有交易、投资、期货、股票、基金等等金融模型。

从测试角度来看金融业软件有两个特点第一对数据精准性要求非常高不能出错bug的成本极大第二软件的设计要求有很强的金融领域知识也很复杂。

所以,金融行业对软件的交付质量要求高,对软件测试非常重视,自动化测试的投入也很大。而自动化测试设计的过程,其实也是对复杂业务梳理的过程。

挑战随之而来用我们的Job模型能不能让金融系统的复杂业务变得简单今天我们就一起结合例子来看一看。

一个转账案例

我们来看后面这个转账案例。

图片

这是一个类型为正例的测试案例。金融业测试里,要设计很多的测试案例,正例是那些预期能够正常完成业务的测试案例,而反例是由于违背了业务规则,预期无法成功完成业务的测试案例。

在我们的测试案例里,交易账户都是正常的状态,交易额度也在安全额度之内,预期结果是能够正常完成转账交易,所以类型是正例。

Job数据建模

了解了案例情况我们想用Job模型来为它建模该怎么做呢

金融业务里最关键的要素就是数据,我们可以从数据角度来理一下测试案例。

这里的数据有账户数据,包括转出账户A和转入账户B;还有交易数据,包括转账类型同行转账和转账金额4000;另外,还有配置数据,单笔转账限额5000和交易超时时间30分钟等。

图片

我们Job模型有三个传递数据的地方分别是JobInput、TestData和TestConfig。

Input是从Job外部传递进来的数据TestData是Job自带的测试数据源能够多组数据循环运行JobTestConfig是Job会引用到的比较固定的配置数据。

想一想账户数据和交易数据需要放在JobInput里还是TestData里呢这个要看我们自动化测试整体设计。

如果我们每次测试运行都要创建新的测试账户那转账Job就可以从Input里获得其他Job创建好的账户信息反之测试账户是存量的数据那我们应该把它放在TestData里实现“多份数据一份代码”这样才能提高转账Job的ROI。

而单笔转账限额和交易超时时间我们把它放在TestConfig里。表格更新如下

图片

Job模型建模如下

图片

Job行为分解

数据研究完了,我们再来看梳理一下测试案例的操作步骤。

1.登录系统
2.发起转账
3.验证转账
4.退出系统

登录系统和退出系统这两个行为是通用的为了提高Job的ROI我们可以把登录系统和退出系统独立出来2个Job。而发起转账和验证这2个步骤是作为1个Job还是分割成2个Job呢。这取决于转账和验证的步骤复杂度在刚开始时我们不要过度工作over work可以把转账+验证先作为一个Job。

这样的话转账Job就分解成了3个子Job登录转账和验证退出。在Web上实现的话我们选择用Selenium作为开发工具。

图片

因为子Job继承了父Job的属性现在3个子Job应该可以从TestData里拿到账户信息登录Job用A账户进行登录转账验证Job使用A账户给B账户转账4000退出Job退出A账户。

现在自动化测试开发人员就可以用Selenium工具进行开发了

Job扩展

自动化测试开发人员开发出这3个Job后就完成了转账自动化测试第一个版本。能实现跑起来的基础目标。

但要想让它稳定运行,我们还需要理清楚金融业务的关联性。比如,银行账户是有复杂的配置的,光账户状态就有几十种,比如时效、锁定、冻结、挂失等等。想要让转账测试运行成功,测试账户必须在正常的状态下,有转账的权限,而且还要有足够的余额等等这些条件。

这些都是转账业务成功运行的前提条件如果不满足这些条件直接开始运行转账Job可能会遇到各种“错误”。但这些“错误”是正常的预期所以诊断它们只会浪费你的时间。

数据验证

怎么办为了保证转账Job的稳定运行我们可以增加一个ValidateJob来验证数据。让转账Job的依赖指向ValidateJob如果ValidateJob运行通过转账Job才会运行否则转账Job就不必运行。

ValidateJob负责做数据验证我们可以用Great Expectation 来完成这个验证。

Greate Expecation是一个Python的数据验证框架它支持多种数据源内嵌规则引擎。为了让你直观了解它的使用过程我为你准备了后面的流程图。

图片

我们的账户存在MySQL数据库里可以在greate_expectation.yaml里配置数据库信息

datasource_yaml = r"""
name: my_mssql_datasource
class_name: Datasource
execution_engine:
  class_name: SqlAlchemyExecutionEngine
  connection_string: mssql+pyodbc://sheng.liu:Welcome1@<HOST>:<PORT>/<DATABASE>?driver=<DRIVER>&charset=utf&autocommit=true
  #要验证数据集是Account表
  table: account

然后就可以在Jupyter Notebook里开发数据验证代码了

#验证账户信息
batch.load(greate_expectation.yaml)
#验证规则1:账户的State字段不为空而且是Active状态
batch.expect_column_values_to_be_equal('State','Active')
#验证规则2:账户余额必须大于4000
batch.expect_column_values_greater_than("balance",4000)

运行代码Great Expectation会从数据库里读取账户数据进行数据验证2条规则都通过整个Job运行成功会产生下面的报告。
图片

现在我们把Validate数据Job添加到Job设计图里更新如下。

图片

这样每次转账Job运行之前都会先运行Validate数据Job。只有数据都准备好了转账Job才会运行。在这个机制的保护下转账Job的运行结果成功或失败就更可信了。

反例测试

有了ValidateJob之后我们可以考虑反例测试了。这里反例的逻辑是这样的如果账户数据是异常的情况下转账Job是会失败的要是交易依然成功那就是一个安全漏洞。

因为这种漏洞在金融业务里很可能带来巨大的经济损失,所以,在金融测试里,反例测试也是很重要的测试案例。

不同的反例条件,转账执行失败的结果也是不一样的,它们有一个对应关系。 我列了一个条件-结果表格,这样你能一目了然。

图片

那自动化设计里,反例测试该怎么做?

你可能在第一时间想到的办法是开发一个“转账Job-反例”在这个反例Job里把上面表格里的条件和结果作为测试案例里的Given条件和Then预期输出。

这就引出了下一个问题这个反例Job的数据从哪里来其实这种情况下我们就可以利用Job模型的Input、Output机制让ValidateJob验证数据之后把数据通过Output分发到转账正例和转账反例的Job中去。

Greate expectation有一个Action的机制根据规则匹配的结果即可触发不同的Action配置文件如下

validation_operators:
    action_list_operator:
        class_name: ActionListValidationOperator
        action_list:
        - name: StateNotSatisfied # 状态不符合
          action:
            class_name: Output
            output: "State not satisified"
        - name: BalanceNotSatisfied # 余额不符合
          action:
            class_name: Output
            output: "Balance not satisified"  

现在我们再次重构Job设计树就会得到下面这个设计结构。我们在跟转账正例Job平行的层级再加上一个转账反例Job。不难看出正例和反例的数据都来自于一个ValidateJob的输出。

图片

ValidateJob专门负责账户的合法性验证使用Greate Expectation工具内嵌规则引擎来加载和解析账户数据并把账户数据分组通过Output输出。然后转账的正例Job拿到合法的数据反例Job拿到非法的数据。

我给你打个比方,帮助你理解一下这个工作原理。其实这个场景就像生产线,第一个工人只管生产螺丝钉,他很专业,可以生产不同型号的螺丝钉,他不需要知道谁会使用这些螺丝钉。他需要做的就是,做好螺丝钉后,就把他们放到传送带上;而第二个工人是造飞机的,他会从传送带上拿到飞机螺丝钉,然后开始自己的任务;第三个工人是造汽车的,他拿到汽车的螺丝钉也开启自己的任务了。

这样分工,每个工人都在干自己的专业工作,又通过传送带和其它工人完成了协作。放在软件设计上,就满足了高内聚,低耦合的设计原则。

咱们接着推演后续可能会怎样发展。如果哪一天ValidateJob里面越来越复杂了一个人无法完成需要分解给10个人去做。那么只需要保持ValidateJob接口不变在ValidateJob下面分解成10个子Job就可以。

就像生产线上生产螺丝钉的人手不够用了他是招8个人还是10个人分成几个小组这些都是这个小组内部的事只要这一组还是跟以前一样对外提供不同型号的螺丝钉就可以了。

让我们回顾一下整个过程。我们从一个银行转账正例案例开始向下细化成3个Selenium Job又平行引入了数据验证Job和反例Job。

到这里你是不是对怎么应用Job模型更熟练了呢我们利用这个模型根据业务需要来扩展Job树。具体扩展方向有两种,一种是向下去下钻,将测试场景分解细化;另一种是向上聚合抽象,把场景归类合并到一起,形成一个更大的场景。

最后我们设计好了正例和反例Job就能聚合一起生成一个转账的正反测试Job。

图片

其实这个Job树并不是“终点”我们还可以根据业务的复杂度再进行细化。

金融系统里数据验证是一个精细的工作确保无误。在手机端、Web端、API甚至DB都要验证数据。根据这个需求我们可以把转账+验证Job进一步分解分解后的情况如下。

图片

你可能还有疑惑不知道要怎么判断我们是否得到了一个足够细化的最终结果。其实判断起来也不难终点就是业务逻辑梳理清楚后这棵Job树的所有叶子结点都明确了实现工具。这样我们就可以推进分工进入开发实现阶段了。

我在反例Job里没有标注蓝色的实现工具你可以考虑一下怎么细化。

小结

在今天的例子里我们通过Job树的设计梳理了金融业务的数据流动和验证。而且由于金融业务的特殊性尤其是对数据精准性的要求。我还带你认识了一款数据验证框架Greate Expecation。它是基于Python语言的框架能够从数据源里获得数据进行规则验证输出结果而且在Jupyter Notebook里开发交互性和效率都很好。

这整个的建模过程经历了从整体到局部从概要到细节的推演。其实直到这棵Job树的叶子结点都有了选型好的工具、可开发的Job我们的交易案例设计才算清楚了这也是业务梳理完毕的信号。

每一个叶子结点的Job就是一个开发任务有接口、有数据、有配置开发工作任务列表就出来了。而且通过Job之间的依赖关系我们也可以得出一个有优先顺序的开发执行计划谁在前谁在后。

今天这一讲为了让表达更形象我们沿用了树形图来推演设计。在实际工作中我们可以把这个设计保存在YAML或XML文件里方便去做验证和二次开发我会在设计篇最后一讲说说这块的实现思路。

下一讲我们会继续锻炼自动化设计思维探索如何利用Job模型实现自动化部署管线的设计敬请期待。

思考题

你的项目里有没有涉及数据验证测试,你是怎么做的?

欢迎你在留言区和我交流互动,也推荐你把今天学的内容分享给更多同事、朋友。