Fork me on GitHub
Google

测试数据与代码分离的测试

测试数据与代码分离的测试

今天看到花花同学写的白盒测试的白皮书,大致翻了下。感觉缺少点什么,之前也一直都在讨论,测试数据与测试代码是否需要分离,测试代码如何建设冗余的问题。如何解决这2个问题,feed4junit是一种思路,我对比junit 4和feed4junit的功能,大致了解这些功能的实现。

对于网站的接口测试,常常面临的一个场景:大量不同的输入,但处理逻辑的业务不变,对其结果进行验证也会有不同的期望值。按照之前的一些思路,往往会为每一组输入和输入写一个case,达成大量的代码冗余。

基于上面的场景,junit 4给出了参数化的特性,从而实现了不同的输入对应相同的代码。

待测的方法:

public class UserAccess {
        // simple validation for user name and password
        public static boolean accessCheck(String userName, String password) {
               if (userName.length() <= 4 || userName.length() > 8 )
                      return false;
               if (password.length() <= 4 || password.length() > 8 )
                      return false;
               if (userName.contains("@" ))
                      return false;
               if (password.contains("*" ))
                      return false;
               return true;
       }
}

1. 使用Junit参数化,减少代码冗余:

测试代码:

import java.util.Arrays;
import java.util.Collection;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import static org.junit.Assert.assertEquals ;

import sample.junit.code.UserAccess;

/*
 * JUnit - Parameter test sample
 */
@RunWith(Parameterized.class)
public class JunitSample {
        private String user ;
        private String pwd ;
        private boolean expected ;

        @Parameters
        public static Collection dataGenerate(){
               return Arrays.asList( new Object[][]{
                           { "user01","123456" ,true},
                           { "user02","123456" ,true},
                           { "user03","12345678" ,true}
              });
       }

        public JunitSample(String user,String pwd,boolean expected){
               this.user = user;
               this.pwd = pwd;
               this.expected = expected;
       }

        @Test
        public void testAccessCheck(){
               assertEquals(expected,UserAccess. accessCheck(user, pwd));
       }
}

运行结果如下所示:

2. 数据与代码的分离(Feed4Junit)

Feed4JUnit 是开源的测试组件,您可以从如下链接下载最新版本: http://sourceforge.net/projects/feed4junit/files/

Feed4JUnit 的数据源可以包括以下几种类型 – 文件 (CSV 或者 Excel )、数据库、自定义数据源。

基本的使用方法: Feed4JUnit 使用一个特殊的运行类 Feeder.class,用来支持与标识参数化测试,如果您想要编写数据与代码分离的测试脚本,必须同时满足下面的三个条件:

  1. 必须在您的测试类上增加注释 @RunWith(Feeder.class) 。
  2. 您需要使用 @Test 来标示您实现测试的方法。
  3. 使用 @Source 来声明和接收数据源的数据。

2.1. 文件数据源(excel和csv差不多)

  • 在测试项目的根目录下创建 data.xls 和data.csv数据文件,第一行会以列名存在,在运行过程中不会作为数据读取。

  • 创建测试类并在接收数据的测试方法上声明数据源为 @Source("Data.xls"),Excel 中的数据在传递过程中会自动按照列与测试方法的参数的位置顺序进行匹配,并以行作为一个单位读取并传递给测试方法体。

    比如上图中的 user 列的值会做为方法的第一个参数传入方法体中,pw 列的值会作为方法的第二个参数,以此类推。在测试进行过程中,首先在 Excel 文件中读取一行(包含三列),接着按照位置顺序将数据传递到方法体中(每列按顺序对应一个参数)进行执行,执行完成后读取 Excel 中的下一行进行相同流程的测试,其原理与 Java 中的迭代器十分类似。

    请注意当数据文件中数据的列数小于测试方法参数的个数的时候,测试会因为位置不匹配而失败。如下所示:

import static org.junit.Assert.assertEquals ;

import org.databene.benerator.anno.Source;
import org.databene.feed4junit.Feeder;
import org.junit.Test;
import org.junit.runner.RunWith;

import sample.junit.code.*;

@RunWith (Feeder.class )
public class Feed4junitSample {
        @Test
        @Source( "Data.csv" )//CSV source
        public void testAccessCheck_CSV(String userName, String pw, boolean expected) {
               assertEquals(expected, UserAccess. accessCheck(userName, pw));
       }

        @Test
        @Source( "data.xls" )//Excel source
        public void testAccessCheck_Excel(String userName, String pw, boolean expected) {
               assertEquals(expected, UserAccess. accessCheck(userName, pw));
       }

}

运行结果如下:

2.2 数据库作为数据源

同样,Feed4junit也支持数据库数据源,这里不做详细的介绍:其原理和上面文件数据源差不多,样例代码如下:

类上声明@Database:

import static org.junit.Assert.assertEquals;
import org.databene.benerator.anno.Database;
import org.databene.benerator.anno.Source;
import org.databene.feed4junit.Feeder;
import org.junit.Test;
import org.junit.runner.RunWith;

import sample.code.UserAccess;

/*
 * Feed4JUnit - Get Data from Database, all test methods can use the database data
 */
@RunWith(Feeder.class)
@Database(
                id = "testdb", 
                url = "jdbc:mysql://localhost:5000/SAMPLE", 
                driver = "com.mysql.jdbc.Driver", 
                user = "admin", 
                password = "admin")
public class F4JfromDB {

        @Test
        @Source(id = "testdb", selector = "select * from TEST")
        public void testAccessCheck(String userName, String pw, String expected) {
                Boolean bSucess = UserAccess.accessCheck(userName.trim(), pw.trim());
                assertEquals(expected.trim(), bSucess.toString());

        }
}

测试方法上声明@Database

@RunWith(Feeder.class)
public class F4JfromDB_Method {
        @Test
        @Database(
                        id = "testdb", 
                        url = "jdbc:mysql://localhost:5000/SAMPLE", 

                        driver = "com.mysql.jdbc.Driver", 

                        user = "admin", 
                        password = "admin")
        @Source(id = "testdb", selector = "select * from TEST")
        public void testAccessCheck(String userName, String pw, String expected) {
                Boolean bSucess = UserAccess.accessCheck(userName.trim(), pw.trim());
                assertEquals(expected.trim(), bSucess.toString());

        }
}

测试运行过程中,通过 url,driver 等信息建立数据连接,通过 selector 发出数据请求,最后完成查询并把数据传递给测试方法,数据在传递给方法的时候,会按数据表的列的顺序与参数进行匹配.

4.最后

Feed4JUnit 提供了对数据与代码分离的测试支持。只需要简单的注释,Feed4JUnit 就可以使用户很方便的实施数据与代码分离之间的测试,对于习惯了JUnit 测试框架的同学来说,是一个十分省力的利器。

Feed4JUnit 同时还提供大量数据的随机测试和等价类测试等众多功能,这个留到后面再进行挖掘。

comments powered by Disqus

top