欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

使用DbUnit做高效的单元测试,dbunit单元测试,DbUnit介绍做单元测

来源: javaer 分享于  点击 3867 次 点评:198

使用DbUnit做高效的单元测试,dbunit单元测试,DbUnit介绍做单元测


DbUnit介绍

做单元测试时,如果测试的对象依赖于数据库或者其他组件,测试的时候就会很费劲,需要mock很多方法。Manuel Laflamme做的开源项目DbUnit框架为依赖于数据库对象的单元测试提供了一种优雅的方案。 使用DbUnit开发人员可以控制数据库的状态,也不需要在每次测试时手动初始化数据。

使用DbUnit的准备工作

第一步我们需要配置DbUnit相关的数据库的结构定义文件,这些文件是xml格式的,例如,我们的数据库里有一个EMPLOYEE表,其结构如下:

dbunit-table

这个表里有一条数据,内容如下:

dbunit data

那么DbUnit需要的数据库表结构定义文件如下

<EMPLOYEE employee_uid='1'           start_date='2001-11-01'                     first_name='Andrew'           ssn='xxx-xx-xxxx'           last_name='Glover' />

上面的xml就是程序中使用的数据库EMPLOYEE表的种子文件。

在实际程序中我们需要创建一系列的表来做为种子文件,如下示例:

<?xml version='1.0' encoding='UTF-8'?><dataset>  <EMPLOYEE employee_uid='1'             start_date='2001-01-01'                     first_name='Drew' ssn='000-29-2030'             last_name='Smith' />  <EMPLOYEE employee_uid='2'             start_date='2002-04-04'                     first_name='Nick' ssn='000-90-0000'             last_name='Marquiss' />  <EMPLOYEE employee_uid='3'             start_date='2003-06-03'                     first_name='Jose' ssn='000-67-0000'             last_name='Whitson' /></dataset>

准备工作就是这样了,下面我们看下如下使用DbUnit

使用DbUnit

DbUnit框架提供了从JUnit的TestCase类继承的DatabaseTestCase类作为测试类的基类。DatabaseTestCase是一个抽象类,它定义了getConnection()getDataSet()两个方法,所有子类必须自己实现这两个方法。

getConnection()方法要返回IDatabaseConnection对象,该对象是对JDBC Connection的封装。如下代码示例:

protected IDatabaseConnection getConnection()   throws Exception {   Class driverClass =      Class.forName("org.gjt.mm.mysql.Driver");   Connection jdbcConnection =      DriverManager.getConnection(      "jdbc:mysql://127.0.0.1/hr", "hr", "hr");   return new DatabaseConnection(jdbcConnection);}

getDataSet()方法期望返回IDataSet对象,本质上就是返回前面定义的种子文件中的xml数据,如下示例:

protected IDataSet getDataSet() throws Exception {   return new FlatXmlDataSet(      new FileInputStream("hr-seed.xml"));}

有了这两个方法的,DbUnit就可以运行默认的操作了;另外DatabaseTestCase类还提供了getSetUpOperation()getTearDownOperation()两个方法,这两个方法可以控制数据的状态。

通常可以通过getSetUpOperation()方法来刷新一下数据的状态,刷新操作可以使用种子文件来更新测试数据库的状态。 getTearDownOperation()不做任何操作。

如下实现:

protected DatabaseOperation getSetUpOperation()   throws Exception {   return DatabaseOperation.REFRESH; }protected DatabaseOperation getTearDownOperation()   throws Exception {   return DatabaseOperation.NONE;}

另外一个非常常用的操作是在getSetUpOperation()方法中执行CLEAN_INSERT,这样就可以删除在种子文件中的记录,然后重新插入记录。

代码示例:

假定我们要做一个人力资源的项目,我们需要为业务层操作Employee的类来写一系列的测试用例,包括创建,获得,编辑,删除Employee等方法,方法的接口定义如下:

public void    createEmployee( EmployeeValueObject emplVo )public EmployeeValueObject    getEmployeeBySocialSecNum( String ssn )public void    updateEmployee( EmployeeValueObject emplVo )public void    deleteEmployee( EmployeeValueObject emplVo )

下面的DbUnit种子文件,命名为employee-hr-seed.xml,会在后面用到

<?xml version='1.0' encoding='UTF-8'?><dataset>  <EMPLOYEE employee_uid='1'             start_date='2001-01-01'                     first_name='Drew' ssn='333-29-9999'             last_name='Smith' />  <EMPLOYEE employee_uid='2'             start_date='2002-04-04'                     first_name='Nick' ssn='222-90-1111'             last_name='Marquiss' />  <EMPLOYEE employee_uid='3'             start_date='2003-06-03'                     first_name='Jose' ssn='111-67-2222'             last_name='Whitson' /></dataset>

我们的测试类为EmployeeSessionFacadeTest, 从DatabaseTestCase继承,并实现了getConnection()getDataSet()方法前者返回数据库连接,后者返回上面定义的种子文件中的DataSet。

如下测试的方法相当的简单,DbUnit帮我们处理了复杂的数据库数据操作的细节,要测试getEmployeeBySocialSecNum()方法只需要简单的传一个种子文件中有的序列号即可,如:“333-29-9999”

public void testFindBySSN() throws Exception{  EmployeeFacade facade = //obtain somehow  EmployeeValueObject vo =     facade.getEmployeeBySocialSecNum("333-29-9999");  TestCase.assertNotNull("vo shouldn't be null", vo);  TestCase.assertEquals("should be Drew",      "Drew", vo.getFirstName());  TestCase.assertEquals("should be Smith",      "Smith", vo.getLastName());}

要测试createEmployee也很简单,我们只需要执行创建操作,并通过getEmployeeBySocialSecNum方法判断插入的数据是否存在就可以了,如下是示例代码片段:

public void testEmployeeCreate() throws Exception{  EmployeeValueObject empVo =        new EmployeeValueObject();  empVo.setFirstName("Noah");  empVo.setLastName("Awan");  empVo.setSSN("564-55-5555");  EmployeeFacade empFacade = //obtain from somewhere  empFacade.createEmployee(empVo);  //perform a find by ssn to ensure existence}

测试updateEmployee()方法需要四个步骤,首先要找到一个要更新的Employee实体,然后更新对象,然后在通过get方法获得Employee,然后看更新是否生效了。

public void testUpdateEmployee() throws Exception{  EmployeeFacade facade = //obtain facade  EmployeeValueObject vo =    facade.getEmployeeBySocialSecNum("111-67-2222");  TestCase.assertNotNull("vo was null", vo);  TestCase.assertEquals("first name should be Jose",     "Jose", vo.getFirstName());  vo.setFirstName("Ramon");  facade.updateEmployee(vo);  EmployeeValueObject newVo =    facade.getEmployeeBySocialSecNum("111-67-2222");  TestCase.assertNotNull("vo was null", newVo);  TestCase.assertEquals("name should be Ramon",    "Ramon", newVo.getFirstName());}

删除方法的测试和更新差不多,首先找到现在存在的Employee实体,然后执行删除,然后尝试获得这个实体,如果没找到就说明删除操作是没有问题的。

public void testDeleteEmployee() throws Exception{  EmployeeFacade facade = //obtain facade  EmployeeValueObject vo =     facade.getEmployeeBySocialSecNum("222-90-1111");  TestCase.assertNotNull("vo was null", vo);  facade.deleteEmployee(vo);  try{       EmployeeValueObject newVo =       facade.getEmployeeBySocialSecNum("222-90-1111");     TestCase.fail("returned removed employee");     }catch(Exception e){      //ignore     }}

就是这么简单,下面我们看下如何在Ant中使用DbUnit

DbUnit in Ant

在Ant中使用DbUnit不需要继承DatabaseTestCase类,需要一个ant task。 它允许在编辑文件时控制数据库连接,Ant的task非常的强大,通过如下声明来定义一个测试的task

<junit printsummary="yes" haltonfailure="yes">  <formatter type="xml"/>  <batchtest fork="yes"            todir="${reports.tests}">    <fileset dir="${src.tests}">      <include name="**/*Test.java"/>    </fileset>  </batchtest></junit>

在DbUnit task中,可以执行测试的数据库连接和需要的种子文件,在每个setup操作中dbunit都会使用种子文件来初始化数据库。

<taskdef name="dbunit"     classname="org.dbunit.ant.DbUnitTask"/><dbunit driver=" org.gjt.mm.mysql.Driver "        url=" jdbc:mysql://127.0.0.1/hr "        userid="hr"        password="hr">    <operation type="INSERT"             src="seedFile.xml"/></dbunit>

可以定义tear down操作,来删除目标数据库中的种子数据,如下定义:

<dbunit driver=" org.gjt.mm.mysql.Driver "        url=" jdbc:mysql://127.0.0.1/hr "        userid="hr"        password="hr">    <operation type="DELETE"            src="seedFile.xml"/></dbunit>

这样Junit task就可以在开始时初始化测试数据库,在测试结束时删除载入的数据了。

<taskdef name="dbunit"          classname="org.dbunit.ant.DbUnitTask"/><!-- set up operation --><dbunit driver=" org.gjt.mm.mysql.Driver "        url=" jdbc:mysql://127.0.0.1/hr "        userid="hr"        password="hr">    <operation type="INSERT"           src="seedFile.xml"/></dbunit><!-- run all tests in the source tree --><junit printsummary="yes" haltonfailure="yes">  <formatter type="xml"/>  <batchtest fork="yes" todir="${reports.tests}">    <fileset dir="${src.tests}">      <include name="**/*Test*.java"/>    </fileset>  </batchtest></junit><!-- tear down operation --><dbunit driver=" org.gjt.mm.mysql.Driver "        url=" jdbc:mysql://127.0.0.1/hr "        userid="hr"        password="hr">    <operation type="DELETE"           src="seedFile.xml"/></dbunit>

本文翻译自: http://www.onjava.com/pub/a/onjava/2004/01/21/dbunit.html

相关栏目:

用户点评