自动化测试平台设计心得

最近利用些业余时间自己编写了一个小型自动化测试框架,在设计过程中自己也渐渐对自动化框架的作用有了些新的认识,希望能和大家分享一下。

其实设计这个框架最初的动机是来源于工作中的一个任务——同事让我维护一个很久以前编写的“自动化脚本”,难度不大,只是一串处理和过程,看懂代码以后只需要修改个别逻辑和参数即可。但后来我想了想,这样纯粹只有过程的脚本,在开发测试时用来当做小工具用不错,但一旦需要建立稳定的自动化测试机制,有大量功能点和测试数据的时候就会显得力不从心。一个功能点对应一个脚本,新增功能点甚至测试数据都需要对应增加脚本,开发维护的成本则会非常高。
后来我自己尝试去做了一个小型的自动化测试框架,虽然花费了不少时间才实现了原来脚本的内容,但是磨刀不误砍柴工,有了框架,接下来新增功能点的开发工作量大大减轻。自己在设计该框架时也基本上是摸着石头过河,一边思考自动化框架究竟需要做什么,一边也参考一些开源自动化框架例如Ruby Watir的设计方式,以下便是我总结的一些经验和心得。


一、测试脚本与测试框架脱离
我开头提到的那个“测试脚本”,从程序启动,测试动作执行,测试结果反馈都一手包干,例如对于A1和A2两个相似的功能点,其测试代码如下:

功能A-1的脚本:A1Test.java
 public static void main(String[] args) throws Exception {
     // A1测试逻辑实现
     ……
     Class.forName("oracle.jdbc.driver.OracleDriver");  
     String url = "jdbc:oracle:thin:@localhost:1521:cui";  
     Connection conn = DriverManager.getConnection(url, "cui", "cui");  
     ……
 }

功能A-2的脚本:A2Test.java
 public static void main(String[] args) throws Exception {
     // A2测试逻辑实现
     ……
     Connection conn = null; 
     try {
     Class.forName("oracle.jdbc.driver.OracleDriver"); 
     String connectionUrl = "jdbc:oracle:thin:@localhost:1521:cui";  
     conn = DriverManager.getConnection(connectionUrl, "cui", "cui"); 
     ……
 }

这样一来A1和A2的测试脚本都可以独立运行,也不需要什么自动化框架,但是原本相似的A1和A2功能,却因为这样的架构要写两次代码,如果TestA1和TestA2由两个不同的程序员编写,那么即便是如上面所示连接一个相同的数据库,每个人需要自己写出实现方式,且都可能有不同的代码风格,这样极大增加了测试代码的编写和维护成本。
对于测试脚本而言,仅仅只需要负责测试逻辑本身,不应该负责诸如脚本启动,管理的功能,同时降低提升测试脚本编写和维护成本,一些公用的方法例如数据库连接等,都最好封装成方法放在测试框架中,然后供测试脚本调用。
我在编写自动化脚本时,将每个测试功能点脚本作为一个Scenario类供测试框架调用的,测试框架可以根据用户输入或者任务设定选择执行哪些脚本。

TestFramework.excute(Scenario userInputScenarioName);

同时,对于如数据库连接查询这样的操作,也都做了封装,在每个Scenario脚本里,编写者可以通过1行代码就能查询想要的数据:
在配置文件中填入数据库信息:

#别名 db_dev #类型 Oracle #IP 127.0.0.1 #端口 1521 #数据库名 db1 #用户名 cui #密码 cui

在脚本文件中就可以这样来访问数据库:

String id = DB("db_dev").getSingleResult("select id from ……");

数据库的链接,关闭工作都由框架进行统一封装。


二、测试数据与测试脚本脱离
测试脚本与测试框架脱离后,测试脚本更加专注于业务逻辑,但仅仅这样是不是就够了呢?对于同一个功能点,我们往往也需要测很多数据,如果每个测试数据都“硬编码”到测试脚本里,那么数据增加的时候又要将硬编码的部分代码复制粘贴,犯了和先前一样的毛病:

// 测试脚本脱离测试框架后的A1Test
class A1Test() {
    ……
    // 测试A的实现过程
    id = DB("db_dev").getSingleResult("select id from …… where coutry = 'CN'");
    Assert(id == 100000);
    id = DB("db_dev").getSingleResult("select id from …… where coutry = 'US'");
    Assert(id == 200000);
    id = DB("db_dev").getSingleResult("select id from …… where coutry = 'CA'");
    Assert(id == 300000);
    ……
}

我们可以看到,虽然测试脚本TestA1的确不再负责程序启动这样的杂事,同时数据链接也更加方便和规范,但是对于多个用例(country值不同,需要校验的id大小不同),仍然需要复制粘贴代码来实现。
因此我们也需要将测试数据从测试脚本中独立出来,实现“一个框架对应多个测试脚本,一个测试脚本对应多个测试数据”。
对于这样的测试数据,往往是相对整齐规范的,我们可以用Excel,txt等文件,用表格的方式存储测试数据,然后写出程序逐行读取(jxl可以支持Excel2003以前Excel文件的读取),每一行就是单次测试所需的数据。

用例ID 用例描述 是否执行 国家缩写 期望结果ID

用例ID用例描述是否执行国家缩写期望结果ID
1测试CN对应IDyCN100000
2测试US对应IDyUS200000
3测试CA对应IDyCA300000

我采用jxl去读取Excel数据,同时再对参数取用的方法进行简化和封装,最终可以用例如param("期望结果")这样的方式返回当前执行的数据行对应列的数据。

// 测试数据与测试脚本脱离后的A1Test
class A1Test() {
    ……
    // 测试A的实现过程
    id = DB("db_dev").getSingleResult("select id from …… where coutry = " + param("国家缩写"));
    Assert(id == param("期望结果ID"));
    ……
}


三、总结
测试脚本从测试框架脱离,即是将“一个测试脚本负责整个测试执行过程”的设计思路变为“测试脚本只负责业务逻辑,一个测试框架驱动多个测试脚本完成测试”。当业务逻辑变化时,我们可以只修改测试脚本和数据而无须修改测试框架,当测试数据需要增加和修改时,我们可以只修改测试数据而无须修改测试脚本,这样的思路归根结底还是来源于面向对象,但不管怎样,提高效率,降低成本才是最终的目的。

转载于:https://www.cnblogs.com/oceanblue/archive/2011/02/20/1959027.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值