Skip to content

第39讲:测试自动化设计模式:一步到位

第 37 讲在讨论基于事件流图的测试设计时,就已经触及基于模型的软件测试(Model-Based Testing,MBT),事件流图、有限状态机等可以被看做是测试模型,基于模型的自动化测试才是更为彻底的自动化测试。因为基于模型的自动化测试可以自动生成测试用例或对应的自动化测试脚本,然后自动执行相应的测试脚本,而日常我们讨论的自动化测试,只能算半自动化测试------测试执行自动化,而脚本的开发还是手工的。

MBT 中的模型是基于需求或业务分析而创建的,之前在第 24 讲"BDD 及其自动化实践"中讨论了:通过 BDD 自动化框架 Cucumber 的 Feature 文件实现可执行的业务规范,直接让需求可执行(活文档)。也就是从软件研发的源头------需求着手,将需求直接转换为自动化测试脚本,真正实现了"一步到位"的自动化测试。

除了这两点之外,我们是不是可以通过设计模式,实现测试数据的自动生成?这些都是本讲要讨论的内容,了解有哪些测试自动化设计模式可以用,如何做到比较彻底的自动化测试,即给人"一步到位"的效果。

基于模型的自动化测试 基于模型的自动化测试,可以从大家熟悉的决策表、因果图开始,有一个工具 BenderBRT ,这个工具提供了因果图辅助设计,并能根据因果图自动生成决策表,从而生成对应的测试用例。说到组合工具,还有微软公司的 PICT (Pairwise Independent Combinatorial Testing tool)和 NIST 的 ACTS(Automated-Combinatorial-Testing-for-Software)能够生成不同强度组合的测试用例。

如果回到我们前面所说的事件流图或状态图,为实用的软件系统编写状态机并不是一件轻松的事情,特别是当状态机本身比较复杂的时候尤其如此,需要投入大量的时间与精力才能描述状态机中的各种状态,所以不得不尝试开发一些工具来自动生成有限状态机的框架代码。例如,基于 Linux 的有限状态机建模工具 FSME(Finite State Machine Editor)等,FSME 能够让用户通过图形化的方式来对程序中所需要的状态机进行建模,并且还能够自动生成用 C++ 或者 Python 实现的状态机框架代码。再比如,专业的分析设计工具 MathWorks 可以基于有限状态机,自动发现发动机或飞机控制程序的缺陷。

同样是基于状态图模型生成测试用例的工具,例如微软的 Spec Explorer,它可以基于 C# 来描述一组规则,并结合一种小型的配置语言 Cord(Coordination Language)生成代码以及选择特定的测试场景,然后 Spec Explorer 能够通过依据所构建的模型自动生成状态图,可以将它们转换成 "二叉树"的树结构,而遍历二叉树的算法是成熟的,这样就可以生成测试用例。

测试用例一旦生成,就可以在单元测试框架中(如 NUnit)独立于模型运行,其中测试序列去控制被测系统(SUT),同时观察待测试系统的返回值,并与预期值进行比较,然后做出判定:测试是通过还是失败。对测试结果的判定是对 SUT 的一个重要反馈,因为测试失败,也不意味着是 SUT 的缺陷,可能模型的预期行为是错的,即模型需要修正。但 MBT 与传统人工测试相比的最大优势就在于模型维护方便,修改模型相对容易,一旦修改结束,测试用例可以重新生成。

下面通过一些直观的展示,让你更好地了解基于模型的测试用例生成的实现。我们知道,状态图可以描述成 5 元组,其测试用例可以表述成 Γ= {Pr , s , r , G, Po},其中:

  • Pr,节点或状态的前置条件
  • s,节点的输入值或触发器
  • r,转换后的一组输出值/结果节点
  • G,为转换的防卫条件集合
  • Po,节点或状态的后置条件

我们还可以用对象限制语言(Object Constrained Language)来描述前置条件和后置条件,如下所示,可以设置不同的前置条件:

在给定前置条件下的后置条件:

生成测试用例的伪代码如下所示:

不仅可以基于 MBT 方法生成测试用例,而且也可以基于规范的接口文档生成测试用例。即使不采用人工智能技术,采用一般的技术也能基于规范的 API 接口文档生成测试用例,因为接口测试设计实质就是要解决接口参数的测试数据设计。例如,接口文档采用 swagger 或 Open API 3.0 规范来描述,GET 接口传 query 参数,POST 请求传 formdata 参数等,这样通过解析 swagger 的 JSon 数据,就可以自动生成测试代码。

GET 接口定义示例:

POST 接口定义示例:

测试数据的自动生成 在测试中,有的时候测试用例就是测试数据,如果能生成这类测试数据,就相当于生成测试用例;而有时是构造业务数据,为功能测试服务,有时是构造大量的数据为性能测试服务。总之,不管是作为测试用例的数据,还是作为支撑测试的数据,把这类数据都可以统称为测试数据

所以,测试数据生成一直是我们所关注的。之前,我们可能借助正则表达式、数据库 SQL 语句、存储过程或 JDBC 接口等批量生成测试数据,现在可以借助一些工具能完成,比如阿里的数据管理 DMS、开源的 TestDataBuilder(Java)、TestData(Python)等。例如,阿里 DMS 可以根据需求选择目标数据库和数据库表,然后配置表的各个列生成方式,如随机、自定义、逻辑依赖、枚举等。

这里以 Python 开发的 TestData(https://github.com/arieb/python-testdata)为例,说明如何生成所需的测试数据。TestData 不仅提供 DictFactory 类来生成数据,还提供特定的扩展功能。每个 Factory 实例均可用于生成用户所需要的特定个数的数据,生成数据可以存储到数据库或基于数据库的文档。

TestData 提供了下列一些类(功能):

  • Factory,所有工厂的基类;
  • DictFactory,允许子类创建具有特定模式、字典的 Factory;
  • ListFactory,在每次迭代时返回给定 Factory 调用中返回的 elements_per_list 项目的列表;
  • Callable,获取可调用对象作为参数,并在每次迭代时返回调用该对象的结果;
  • DependentCallable,获取可调用对象作为参数,并在每次迭代时,返回作为参数传递的对象的调用结果;
  • ClonedField,复制另一个 Factory 值的 Factory;
  • RandomDateFactory,生成两个日期之间的随机日期;
  • DateIntervalFactory,从基数开始生成日期时间对象,同时在每次迭代中向其添加增量;
  • RelativeToDatetimeField,相对于另一个 datetime 字段生成 datetime 对象。

下面是一个测试数据生成的示例。

sql
# {'start_time': datetime.datetime(2013, 12, 23, 13, 37, 1, 591878), 'end_time': datetime.datetime(2013, 12, 23, 13, 57, 1, 591878), 'event_code': 'USER_CONNECTED'}
# {'start_time': datetime.datetime(2013, 12, 23, 13, 49, 1, 591878), 'end_time': datetime.datetime(2013, 12, 23, 14, 9, 1, 591878), 'event_code': 'USER_LOGIN'}
# {'start_time': datetime.datetime(2013, 12, 23, 14, 1, 1, 591878), 'end_time': datetime.datetime(2013, 12, 23, 14, 21, 1, 591878), 'event_code': 'USER_DISCONNECT'}

模糊测试(Fuzz Testing)方法,一方面可以看做是 MBT,另一方面它通过模糊控制器生成测试数据,即通过一个自动产生数据的模板或框架(称为控制模糊器)来构造或自动产生大量的、具有一定随机性的数据作为系统的输入,从而检验系统在各种数据情况下是否会出现问题。它最早是由威斯康星州的麦迪逊大学 Barton Miller 教授开发一个基本的命令行模糊控制器,以测试 Unix 程序,即通过这个模糊控制器产生大量的随机数据来"轰炸" Unix 程序直至其崩溃。之前模糊测试应用不多,而当互联网应用越来越普遍时,软件系统的安全性成为人们关注的焦点,模糊测试方法又重新得到重视。

模糊测试方法可以模拟黑客来对系统发动攻击测试,在安全性测试上发挥作用之外,还可以用于服务器的容错性测试。模糊测试方法缺乏严密的逻辑,不去推导哪个数据会造成系统破坏,而是设定一些基本框架,在这框架内产生尽可能多的杂乱数据进行测试,发现一些意想不到的系统缺陷。在模糊化的过程中,测试数据会随着对可疑行为的进一步了解而不断完善。例如,HTTP 客户端发出的请求最初包含了随机数据,随后可能会增加各种已知的有效数据或错误数据来进行更深入的验证。

模糊测试一般分为两类,也就是根据产生数据的方式不一样来分类的:

  • 变异测试(Mutation-based Fuzzers),通过字符替换、翻转、数据删除、数据增加等变异技术实现;
  • 生成测试(Generation-based Fuzzers),基于符合协议规范的消息模型(数据模型) 从零开始构建异常信息。

通过上述讨论,可以认识到,要达到测试自动化"一步到位"的水平,有三种主要的自动化设计模式来实现,即:

  • 用一种特定的领域语言,比如 BDD 的 GWT 格式、Open API 规范或其他标记性语言来描述需求文档,基于需求文档生成自动化测试脚本,让需求成为活文档;
  • 将测试需求抽象成模型,比如事件流图、状态图等,然后基于模型生成测试用例;
  • 基于开源工具、自定义工具或模糊测试工具等生成所需的测试数据。

最后出一个思考题,只要准备好软件产品的需求文档,剩下的测试工作都由工具来完成,这一天会到来吗?什么时候能到来?如果不能到来,最大的障碍是什么?