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.

11 KiB

38 | 测试数据的“银弹”- 统一测试数据平台(下)

你好,我是茹炳晟,今天我分享的主题是:“测试数据的“银弹”之统一测试数据平台(下)”。

在上一篇文章中我和你分享了测试数据准备1.0时代的实践在这个1.0时代测试数据准备的最典型方法是将测试数据准备的相关操作封装成数据准备函数。今天我将继续为你介绍测试数据准备的2.0和3.0时代的实践,看看创建测试数据的方法,又发生了哪些变革。

在1.0时代,为了让数据准备函数使用更方便,避免每次调用前都必须准备所有参数的问题,我和你分享了很多使用封装函数隐藏默认参数初始化细节的方法。

但是这种封装函数的方式也会带来诸如需要封装的函数数量较多、频繁变更的维护成本较高以及数据准备函数JAR版本升级的尴尬。所以为了系统性地解决这些可维护性的问题我们对数据准备函数的封装方式做了一次大变革也由此进入了测试数据准备的2.0时代。

测试数据准备的2.0时代

在测试数据准备的2.0时代数据准备函数不再以暴露参数的方式进行封装了而是引入了一种叫作Builder Pattern生成器模式的封装方式。这个方式能够在保证最大限度的数据灵活性的同时提供使用上的最大便利性并且维护成本还非常低。

事实上如果不考虑跨平台的能力Builder Pattern可以说是一个接近完美的解决方案了。关于什么是“跨平台的能力”我会在测试数据准备的3.0时代中解释这里先和你介绍我们的主角Builder Pattern。

Builder Pattern是一种数据准备函数的封装方式。在这种方式下当你需要准备测试数据时不管情况多么复杂你一定可以通过简单的一行代码调用来完成。听起来有点玄乎没关系看完我列举的这些实例你马上就可以理解了。

实例一你需要准备一个用户数据而且对具体的参数没有任何要求。也就是说你需要的仅仅是一个所有参数都可以采用默认值的用户。那么在Builder Pattern的支持下你只需要执行一行代码就可以创建出你需要的这个所有参数都是默认值的用户了。这行代码就是

UserBuilder.build();

实例二你现在还需要一个用户但是这次需要的是一个美国的用户。那么这时在Builder Pattern的支持下你只用一行代码也可以创建出这个指定国家是美国而其他参数都是默认值的用户。这行代码就是

UserBuilder.withCountry("US").build();

实例三你又需要这样一个用户数据英国用户支付方式是Paypal其他参数都是默认值。那么这时在Builder Pattern的支持下你依然可以通过一行简单的代码创建出满足这个要求的用户数据。这行代码就是

UserBuilder.withCountry("US").withPaymentMethod("Paypal").build();

通过这三个实例你肯定已经感受到相对于1.0时代的通过封装函数隐藏默认参数初始化的方法来说Builder Pattern简直太便利了。

趁热打铁我再来和你总结一下Builder Pattern的便利性吧

  • 如果仅仅需要一个全部采用缺省参数的数据的话你可以直接使用TestDataBuilder.build()得到;
  • 如果你对其中的某个或某几个参数有特定要求的话,你可以通过“.withParameter()”的方式指定,而没有指定的参数将自动采用默认值。

这样一来,无论你对测试数据有什么要求,都可以以最灵活和最简单的方式,通过一行代码得到你要的测试数据。

在实际工程项目中随着Builder Pattern的大量使用又逐渐出现了更多的新需求为此我归纳总结了以下4点

  • 有时候,出于执行效率的考虑,我们不希望每次都重新创建测试数据,而是希望可以从被测系统的已有数据中搜索符合条件的数据;
  • 但是,还有些时候,我们希望测试数据必须是全新创建的,比如需要验证新建用户首次登录时,系统提示修改密码的测试场景,就需要这个用户一定是被新创建的;
  • 更多的时候,我们并不关心这些测试数据是新创建的,还是通过搜索得到的,我们只希望以尽可能短的时间得到需要的测试数据;
  • 甚至还有些场景我们希望得到的测试数据一定是来自于Out-of-box的数据。

为了能够满足上述的测试数据需求我们就需要在Builder Pattern的基础上进一步引入Build Strategy的概念。顾名思义Build Strategy指的是数据构建的策略。

为此我们引入了Search Only、Create Only、Smart和Out-of-box这四种数据构建的策略。这四类构建策略在Builder Pattern中的使用很简单只要按照以下的代码示例指定构建策略就可以了

UserBuilder.withCountry(“US”).withBuildStrategy(BuildStrategy.SEARCH_ONLY.build();
UserBuilder.withCountry(“US”).withBuildStrategy(BuildStrategy.CREATE_ONLY).build();
UserBuilder.withCountry(“US”).withBuildStrategy(BuildStrategy.SMART).build();
UserBuilder.withCountry(“US”).withBuildStrategy(BuildStrategy.OUT_OF_BOX).build();

结合着这四类构建策略的代码,我再和你分享一下,它们会在创建测试数据时执行什么操作,返回什么样的结果:

  • 当使用BuildStrategy.SEARCH_ONLY策略时Builder Pattern会在被测系统中搜索符合条件的测试数据如果找到就返回否则就失败这里失败意味着没能返回需要的测试数据
  • 当使用BuildStrategy.CREATE_ONLY策略时Builder Pattern会在被测系统中创建符合要求的测试数据然后返回
  • 当使用BuildStrategy.SMART策略时Builder Pattern会先在被测系统中搜索符合条件的测试数据如果找到就返回如果没找到就创建符合要求的测试数据然后返回
  • 当使用BuildStrategy.OUT_OF_BOX策略时Builder Pattern会返回Out-of-box中符合要求的数据如果在Out-of-box中没有符合要求的数据build函数就会返回失败

由此可见引入Build Strategy之后Builder Pattern的适用范围更广了几乎可以满足所有的测试数据准备的要求。

但是不知道你注意到没有我们其实还有一个问题没有解决那就是这里的Builder Pattern是基于Java代码实现的如果你的测试用例不是基于Java代码实现的那要怎么使用这些Builder Pattern呢

在很多大型公司测试框架远不止一套不同的测试框架也是基于不同语言开发的比如有些是基于Java的有些是基于Python的还有些基于JavaScript的。而非Java语言的测试框架想要使用基于Java语言的Builder Pattern的话往往需要进行一些额外的工作比如调用一些专用函数等。

我来举个例子吧。对于JavaScript来说如果要使用Java的原生类型或者引用的话你需要使用Java.type()函数而如果要使用Java的包和类的话你就需要使用专用的importPackage()函数 和 importClass() 函数。

这些都会使得调用Java方法很不方便其他语言在使用基于Java的Builder Pattern时也有同样的问题。

但是我们不希望、也不可能为每套基于不同开发语言的测试框架都封装一套Builder Pattern。所以我们就希望一套Builder Pattern可以适用于所有的测试框架这也就是我在前面提到的测试准备函数的“跨平台的能力”了。

为了解决这个问题测试数据准备走向了3.0时代。

测试数据准备的3.0时代

为了解决2.0时代跨平台使用数据准备函数的问题我们将基于Java开发的数据准备函数用Spring Boot包装成了Restful API并且结合Swagger给这些Restful API提供了GUI界面和文档。

这样一来我们就可以通过Restful API调用数据准备函数了而且由于Restful API是通用接口所以只要测试框架能够发起http调用就能使用这些Restful API。于是几乎所有的测试框架都可以直接使用这些Restful API准备测试数据。

由此测试数据准备工作自然而然地就发展到了平台化阶段。我们把这种统一提供各类测试数据的Restful API服务称为“统一测试数据平台”。

最初统一测试数据平台就是服务化了数据准备函数的功能并且提供了GUI界面以方便用户使用除此以外并没有提供其他额外功能。如图1所示就是统一测试数据平台的UI界面。

图1 最初的统一测试数据平台UI界面

后来随着统一测试数据平台的广泛使用我们逐渐加入了更多的创新设计统一测试数据平台的架构也逐渐演变成了如图2所示的样子。

图2 演变后的统一测试数据平台架构

接下来,我和你分享一下统一测试数据平台的架构设计中最重要的两个部分:

  1. 引入了Core Service和一个内部数据库。其中内部数据库用于存放创建的测试数据的元数据Core Service在内部数据库的支持下提供数据质量和数量的管理机制。

  2. 当一个测试数据被创建成功后为了使得下次再要创建同类型的测试数据时可以更高效Core Service会自动在后台创建一个Jenkins Job。这个Jenkins Job会再自动创建100条同类型的数据并将创建成功的数据的ID保存到内部数据库当下次再请求创建同类型数据时这个统一测试数据平台就可以直接从内部数据库返回已经事先创建的数据。
    在一定程度上这就相当于将原本的On-the-fly转变成了Out-of-box缩短整个测试用例的执行时间。当这个内部数据库中存放的100条数据被逐渐被使用导致总量低于20条时对应的Jenkins Job会自动把该类型的数据补足到100条。而这些操作对外都是透明的完全不需要我们进行额外的操作。

这就是测试数据准备的3.0时代的最佳实践了。关于这个统一测试数据平台,如果你还想了解更多的技术细节,欢迎你给我留言,我们一起讨论。

总结

我和你分享了测试数据准备2.0时代的Builder Pattern实践以及3.0时代的统一测试数据平台。

2.0时代的Builder Pattern在提供了最大限度的数据灵活性的同时还保证了使用上的最大便利性并且维护成本还非常低。如果不考虑跨平台能力的话Builder Pattern已经是一个接近完美的解决方案了。

3.0时代统一测试数据平台其实是将所有的数据准备函数在Spring Boot的支持下转变为了Restful API为跨平台和跨语言的各类测试框架提供了统一的数据准备方案。

思考题

关于统一测试数据平台由于引入了Core Service和内部数据库所以可以在此基础上实现更多的高级功能。对此你觉得还可以引入哪些功能呢

感谢你的收听,欢迎你给我留言。