本系列其他文章:
第一部分:测试开发之路 ---- 框架中数据的管理策略
第二部分:测试开发之路 ---- 数据驱动及其变种
第三部分:测试开发之路 ---- 可读性,可维护性,可扩展性
第四部分:测试开发之路 ---- 可读性,可维护性,可扩展性 (续)


第一个要讲的是测试数据的管理,因为在我以往的公司中,我发现这方面是大家最容易忽略,但却是影响成败的最关键的几个因素之一。

1. 数据的分类

  说到测试数据,我想大家第一个反应就是数据库。现如今大多数产品,尤其是互联网产品都是使用数据库来保存数据的。以下的例子中,我均已数据库为例。那么在测试中,测试数据的准备和维护就成了一个很重要的方面。同样的,自动化测试中,测试数据的管理也成为了一个难点。管理不好,你根本没法自动化,因为你的脚本不能够重复执行,注意重复执行的重要性。不能重复执行的脚本,不配称为自动化。 也是不能加入持续集成流程中的。这也是我痛恨的那些为了 KPI 而被创造出来的,高大上的,“所谓的测试平台” 的特性。

  好了,既然提到了测试数据,那么我们先把测试数据分个类吧。

按使用方式分类

  1. 共享数据
  2. 隔离数据。
共享数据:所有的 case 或者是一些 case 共同使用的测试数据。
  1. 优点:速度快,数据只需要创建一次就可以给很多 case 使用。
  2. 缺点:

OK,貌似我们看到了使用共享数据的方式除了速度快以外貌似没什么出众的地方了,而且缺点貌似也很严重,大家会不会想,那么我们就不要用这种方式了好不好? 其实也是不对的,只要使用得当,这种方式在某些场景下也是比较好的方案。具体我后面再说

隔离数据:每个 case 都有独享测试数据,case 之间互不影响,说白了就是为每个 case 都做 setup 和 teardown 的操作。case 执行前创造数据,执行后销毁数据。

按创建数据的类型分类:

调用开发接口:

  好吧我说了太多这种方式的缺点,因为我曾经是真的被坑的很惨。 所以我是不建议用这种方式的。

直接使用 sql:就是直接写 sql 创造和销毁数据。

  好了这种分类我也说完了。可以看出我是比较偏向使用 sql 创造数据的。第一种分类的时候虽然我说共享数据比较坑,但是在某些场景中还是很有用的。但是第二种分类中,我十分的强烈的不建议使用调用开发接口创造数据的方式。case 一旦多了,一个 bug 就能让你崩溃。想象一下如果 case 库里有 5 千条脚本,一个 bug 搞出了 100 以上的脚本 fail 是什么感觉吧(我们曾经是 7 千,迭代一开始,就得专门找俩人维护脚本,什么也不干)。

  OK,说完了两种分类,那么我们说说如何在我们的测试中使用这些数据管理方式吧。想了想,还是以我现在给我们公司写的这个框架举例子好了。

in-line 方式:

  其实我是实在不知道该怎么给这种方式起名了,所以借用了 xunit test partten 一书中的定义。而且我也实在不知道 in-line 方式翻译成中文该怎么说,所以就这么叫它吧。它的使用方式很简单,也很原始。 你直接在脚本中,或者在各种框架中的 setup 方法,teardown 方法中直接写代码创建这些数据。例如 testng 中就有 beforeMethod,beforeClass 以及 beforeSuit 等标签帮你初始化测试环境。你可以使用原始的 JDBC 语句,也可以使用例如 mybatis 这一类的 ORM 框架。强烈建议用 ORM,配置个级联操作直接给脚本调用就行了。在脚本里写 sql 简直是噩梦。

注册式:

  这个方式不太好理解,什么叫注册式呢?就是你把测试数据注册到框架中,并说明是共享数据,还是隔离数据。这些数据的作用域是什么。接下来的事情就交给框架做了,框架帮你在测试执行前创建测试数据,测试结束后销毁数据。完全不用测试人员关心。这是我比较推崇的方式,我在接口测试中使用的就是注册式加 in-line 方式管理数据的,说明一下,用的是直接写 sql 的方式入库的,只不过 sql 由框架生成,我们不用管。 好了,这么说实在有点难懂。 让我们来看一下例子吧。

  看过我在 Tester Home 的第一篇帖子的人应该对注册式数据管理有印象。 没看过的可以看一下,下面是链接:
NO_CODE 接口测试框架

  在我的框架中,我创建了一个叫做 DataBaseFile 的注解(注解为 java 语言特性,非 java 工程师请 google),注解里定义了两个属性,一个叫 filePath。规定了数据文件的路径,这个数据文件做什么的呢?看下面截图。

  这是一个 excel 文件,大家看这个是不是很像数据库的表结构。 答案是对了,其实这个文件就是从 navicat for mysql 导出来的。我们的思路是在 UI 上创建数据,然后用像 navicat 这种数据库客户端导出一个 excel 文件。框架会读取这个文件拼出三种 sql 并封装在一个对象里。这三种 sql 分别是 select,insert,delete。这三种 sql 就可以用来维护我们的测试数据了。这样就解决了测试人员写 sql 的问题。
  OK,让我们看看 DataBaseFile 注解的第二个属性 ---- 作用域。这个属性是一个枚举类型,这个枚举暂时有两种值,method 和 class。其实这个属性规定了测试数据的作用域。如果测试类指定了 method 作用域,那么这个数据就是 “隔离数据”,框架会在每一个测试用例执行前创建数据,测试结束后删除数据。如果指定了 class 作用域,那么框架会在这个类的所有测试方法开始前创建数据,所有的测试结束后销毁数据。其实这就是 “共享数据” 了,测试类中所有的 case 共享这些数据。 有些时候这个策略是比较有用的。 比如被测功能不会对数据库造成影响的情况,例如查询某些订单的功能。测试这个功能的一批用例,就是可以使用同样的数据的,只不过可能我这个用例是按 id 查,下个用例按 name 查,其他的可能对查询结果做正序排序或者倒叙排序。所以我在一开始也说共享数据在某些情况下也是蛮有用的。其实还应该有个 suit 作用域,只不过我暂时没用上。

  OK,知道了这些是不是就比较好设计实现了。 只需要在 testng 的各种 before 方法上做手脚就可以了。 具体思路就是创建一个基类,在基类里维护一个 List,list 里装的就是读取 excel 文件后分析出的三种语句的集合。在 before 和 after 方法里使用 java 反射技术读取子类的注解。这样就可以控制子类的测试行为了。 是不是还不算复杂?

  抱歉现在在丈母娘家,用的媳妇电脑。源代码在公司的电脑里呢,51 过后我把具体实现贴上来吧。

transaction roll back:

  这种模式是用来销毁测试数据的不二神器,专门在单元测试和小型集成测试中用来销毁测试数据的。 不过此模式依赖软件设计支持这种模式。也是就是大家说的需要软件有可测试性。具体怎么搞呢,就是要求开发在设计的时候就把测试考虑进去,不能在持久化层就把事务写死在被测方法里。而是把事务专门提取出一层来,或者使用 spring 这种提供了事务管理的框架管理软件的事务。这样的话,我们就可以在测试脚本中显示的控制事务。 在测试结束的时候,直接一个 roll back 就搞定了。 管你被测功能到底对数据库做了什么,我不 care~,直接暴力的全部回滚。 注册式数据管理方式的缺点就是它无法删除被测功能本身创造出来的数据。需要使用 in-line 的方式显示的删除这部分数据。 但是 transaction roll back 就方便多了。我心中完美的管理方式其实就是注册式加上 transaction roll back。 所以现在我们都要求开发再设计之初就考虑到这方面的事。 这也是方便他们做单元测试。 可惜这种方式不能用在接口测试和 UI 自动化中。

  之后我会在可测试性那一章中,专门提及 transaction roll back 的,等不及的同学可以自行 google 这种模式。它出自 xunit test pattern。

  OK,今天先说到这吧,媳妇叫我了。 以后如果有补充的我在修改帖子。 感谢大家的阅读。


↙↙↙阅读原文可查看相关链接,并与作者交流