DbUnit 简介

   本文主要为对dbunit getting started 简单理解,其他参考文章见文末。

1. 为什么要用DbUnit

      dbunit(官网:http://www.dbunit.org/)是一种扩展于JUnit的数据库驱动测试框架,它使数据库在测试过程之间处于一种已知状态,如果一个测试用例对数据库造成了破坏性影响,它可以帮助避免造成后面的测试失败或者给出错误结果。这是官方的解释,个人理解dbunit的好处是如果一个测试用例影响了数据库,或者说数据库被发生了变动,dbunit可以很容易地发现问题,并使其他测试用例不受影响,也就是一种已知并可控的状态。

      dbunit属于极限编程(Extreme Programming)范畴,推荐测试优先的开发和持续集成。个人觉得dbunit最大的好处就在这“持续集成”上。以前只要数据库发生变化,比如改了表结构,哪怕仅仅添加一个字段,后续工作也不少,已经写完的相关DAO必须得改,测试用例也要改,改了数据库忘记改代码的情况也不少见。这还是对一个人来说,如果是多人合作,你很大可能会碰到这种情况:boss指着屏幕质问你:为什么10分钟前测试通过的功能,现在连界面都看不到...于是你装无辜,装可怜,查日志,查代码,最后只能无力地怒吼:谁动了我的数据.....很多时候单元测试只不过是一种摆设,少有项目完成后还可以完整运行的测试方法,如果你能迅速地跑起测试用例,问题自然暴露,让我们来看看dbunit如何来解决这些问题。

 

 

 

2.DbUnit原理

      dbunit通过维护真实数据库与数据集(DataSet)之间的关系来发现与暴露测试过程中的问题。此处DataSet可以自建,可以由数据库导出,并以多种方式体现,xml文件、XLS文件和数据库查询数据等,一般多用XML文件。在测试过程中,DataSet被称为期望结果(expected result),真实数据库被称真实结果(actual result),你所要做的就是通过dbunit完成期望结果与真实结果之间的操作与比较,从而发现问题和校验结果。

    dbUnit包括三个核心部分(详见:http://www.dbunit.org/components.html#cleanInsert):

        1)IDatabaseConnection :描述dbunit数据库连接接口;

        2)IDataSet:数据集操作接口;

        3)DatabaseOperation:描述测试用例测试方法执行前与执行后所做操作的抽象类;

     值得关注的是 DatabaseOperation的各种实现,比较常用的有 REFRESH、DELETE_ALL和CLEAN_INSERT等。这些操作关系到数据集与数据库数据的同步、数据准备,不小心就会对数据库原有数据造成影响,所以务必做好备份。

    DatabaseOperation.REFRESH:同步DataSet数据到目标数据库,DataSet中有database中也有的数据,会从DataSet更新到database,DataSet中有database中没有的数据会插入到database中。对database中有DataSet中没有的数据不造成影响,适用于database中有其他数据的情况;

    DatabaseOperation.DELETE_ALL:删除database中DataSet有的所有表数据,DataSet中没有的表不造成影响。

    DatabaseOperation.CLEAN_INSERT:先进行DELETE_ALL操作,在把DataSet中有database中没有的数据插入database;

 

3.DbUnit应用流程

    1) 准备DataSet;

       即准备测试数据(expected result),可以手写,也可以直接从数据库中导出,以XML文件为列,导出代码如下:

 public static void main(String[] args) throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        Connection conn = DriverManager.getConnection(
                "jdbc:mysql://localhost/ecshop", "root", "");
        IDatabaseConnection connection = new DatabaseConnection(conn);
        QueryDataSet dataSet = new QueryDataSet(connection);
        //将整个ecs_card表里的数据导出到 xml文件里
        dataSet.addTable("ecs_card");
        FlatXmlDataSet.write(dataSet, new FileOutputStream("ecs_card.xml"));
	//导出数据库所有表
       //IDataSet dataSet= connection.createDataSet();
      // FlatXmlDataSet.write(dataSet, new FileOutputStream("allTable.xml"));

} 

 

2)继承dbunit的测试基类DBTestCase ,建立数据库与数据集的连接;

   dbunit封装了四种数据库连接配置方式:PropertiesBasedJdbcDatabaseTester、JdbcBasedDBTestCase、JndiBasedDBTestCase和DataSourceBasedDBTestCase,默认使用PropertiesBasedJdbcDatabaseTester。最好在你的测试类构造方法中配置。DataSet加载以重载基类 getDataSet()方法实现,代码如下:

public class SimpleTest extends DBTestCase
{
    public SimpleTest(String name)
    {
        super( name );
         System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_DRIVER_CLASS, "com.mysql.jdbc.Driver" ); 
         System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_CONNECTION_URL, "jdbc:mysql://localhost/ecshop?useUnicode=true&characterEncoding=GBK" ); 
        System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_USERNAME, "root" ); 
        System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_PASSWORD, "" );
    // System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_SCHEMA, "" );
    }
    @Override
    protected IDataSet getDataSet() throws Exception
    {
        return new FlatXmlDataSetBuilder().build(new FileInputStream("ecs_card.xml"));
    }
}
  3) (可选) 实现基类 getSetUpOperation() and getTearDownOperation() 方法;

public class SimpleTest extends DBTestCase
{
    ...
    @Override 
    protected DatabaseOperation getSetUpOperation() throws Exception { 
        logger.info("setup REFRESH operation...."); 
        return DatabaseOperation.REFRESH; //刷新dataset 数据到 database 
        // return     DatabaseOperation.REFRESH; // 
    } 
    @Override 
    protected DatabaseOperation getTearDownOperation() throws Exception { 
        logger.info("teardown NONE operation...."); 
        return DatabaseOperation.NONE; //do nothing 
    }
    ...
}

 4) 编写测试方法testXXX(),完整测试用例如下:

public class SimpleTest extends DBTestCase{
    private static Log logger = LogFactory.getLog(SimpleTest.class);


    public SimpleTest() {
    
    }

    public SimpleTest(String name) {
        super(name);
        System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_DRIVER_CLASS, "com.mysql.jdbc.Driver" );
        System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_CONNECTION_URL, "jdbc:mysql://localhost/ecshop?useUnicode=true&characterEncoding=GBK" );
        System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_USERNAME, "root" );
        System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_PASSWORD, "" );
	// System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_SCHEMA, "" );
    }

    @Override
    protected IDataSet getDataSet() throws Exception {
        logger.info("load xml data....");
        return new FlatXmlDataSetBuilder().build(new FileInputStream("ecs_card.xml"));  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    protected DatabaseOperation getSetUpOperation() throws Exception {
        logger.info("setup clean_insert operation....");
        return DatabaseOperation.REFRESH;    //
        // return DatabaseOperation.REFRESH;    //
    }

    @Override
    protected DatabaseOperation getTearDownOperation() throws Exception {
         logger.info("teardown NONE operation....");
        return DatabaseOperation.NONE;    //do nothing
    }


    /**
     * 数据库与备份库比较
     * @throws Exception
     */
    public void testdb() throws Exception{
        //取数据库表
        IDataSet  dataSet = getConnection().createDataSet();
        ITable dbTable = dataSet.getTable("ecs_card");
        //取XML备份表
        ITable  xmlTable = getXmlTable("ecs_card","ecs_card");
        Assertion.assertEquals(xmlTable,dbTable);
    }

    /**
     * 测试表结构
     * @throws Exception
     */
    public void testDbStructure() throws Exception {
         //取数据库表
        IDataSet  dataSet = getConnection().createDataSet();
        ITable dbTable = dataSet.getTable("ecs_card");
        //取XML备份表
        ITable  xmlTable = getXmlTable("ecs_card1","ecs_card");
        dbTable = DefaultColumnFilter.includedColumnsTable(dbTable,xmlTable.getTableMetaData().getColumns());

        Assertion.assertEquals(xmlTable,dbTable);
    }

    /**
     * 测试插入一条记录
     * @throws Exception
     */
    public void testInsertRecord() throws Exception {
        /*这里dao 插入一条记录,省略*/
        IDataSet  dataSet = getConnection().createDataSet();
        ITable dbTable = dataSet.getTable("ecs_card");
        //取XML期望表
        ITable  xmlTable = getXmlTable("ecs_card_exp","ecs_card");
        Assertion.assertEquals(xmlTable,dbTable);
    }

    public void testQueryRecord() throws Exception {
        /*这里dao 查询,省略*/
        String sql = "SELECT *FROM ecs_card ";
        ITable  dbTable = (ITable) getConnection().createQueryTable("ecs_card",sql);
        assertEquals(1,dbTable.getRowCount());

        String cardName = (String) dbTable.getValue(0,"card_name");
        assertEquals("贺卡",cardName);
    }

    /**
     * 获取XML TABLE,默认取当前目录
     * @param xmlFileName
     * @param tableName
     * @return
     * @throws Exception
     */
    private ITable getXmlTable(String xmlFileName,String tableName) throws Exception {
         //取XML备份表
        FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();
        builder.setColumnSensing(true);
        IDataSet xmlDataSet = builder.build(new FileInputStream(xmlFileName+".xml"));
        return xmlDataSet.getTable(tableName);
    }
}

 4. 依赖包

      dbunit所有依赖包见: http://www.dbunit.org/dependencies.html,可直接下载。 简单功能当然不用全部,包含junit、slf4j和commons几个包就可以了。
 5. 参考文章
     dbunit getting started: http://www.dbunit.org/howto.html;

你可能感兴趣的:(DbUnit 简介)