`
Virgo_S
  • 浏览: 1153603 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

DBUnit 进行单元测试

阅读更多
现实系统中通常会有一些具有外部依赖性的对象,这些对象和数据库或者其他对象存在诸多关联。如果我们对这样的对象编写单元和组件级测试的话,可以想象将是非常麻烦的一件事.因为这种外部依赖性的存在,使的我们很难将对象孤立出来进行测试。经常提及的白盒测试法,基本上就是通过控制对象的外部依赖性来达到隔离对象的目的,使的可以操作这些对象的状态和相关行为。  
运用 模拟对象(mock objects) 或者stubs,就是一个控制对象外部依赖性的解决方案。通过隔离那些关联的数据库访问类,象JDBC的相关操作类,对于控制对象外部依赖性将是很有效的。但模拟对象的解决方案对一些特殊的应用系统架构就显得力不从心了,象那些运用了EJB的CMP(container-managed persistence)或者 JDO(java Data Objects)的应用系统架构,在这些架构里,数据库的访问对象是在最底层的而且很隐蔽。  
由Manuel Laflamme编写的开放源代码的DBUnit架构体系,对于控制系统内部的数据库依赖性提供了一个非常不错的解决方案。他允许程序员在整个的测试过程中自由的管理控制数据库的状态,这很重要。利用DBUnit,在测试之前,我们可以给目标数据库植入我们需要的数据集,而且,在测试完毕后,数据库完全能够回溯到测试前的状态。  
  在很多成功的软件项目中,测试自动化往往是关键的层面。DBUnit允许开发人员创建测试用例代码,在这些测试用例的生命周期内我们可以很好的控制数据库的状态。而且,这些测试用例是很容易实现自动化的。这样在测试过程中我们无须对它进行人工的干预,为人工干预造成的后果而担心就更没必要了。  
  简单介绍
  配置使用DBUnit的第一步我们首先需要知道如何生成数据库schema,这个文件是XML格式的,其中包括了数据库的表及相关数据信息。  
  例如,这里有一个数据库表employee,我们可以用SQL的形式这样将他表示出来。而且,我们可以看到,一个简单的数据集可以这样表示在DBUnit中,上面这个表和抽样数据信息可以用XML文件的形式这样表示:
<EMPLOYEE employee_uid='1' start_date='2001-11-01' first_name='Andrew' ssn='xxx-xx-xxxx' last_name='Glover' /> 

这个生成的XML格式的文件可以作为系统所需的所有种子文件(seed files)的样本模版.
  为相互关联的测试场景创建多个种子文件是一个很有效的策略,就象通过不同的数据库文件来区分隔离数据库状态是一个道理。多种子文件策略可以将我们的测试目标锁定到较小的范围,目标数据可以只针对数据库的表,而不是整个数据库。  
  为了给目标数据库植入不同的职员记录,我们需要的XML数据文件如下所示:
<?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和我们所需的数据库schema一起工作了,对于程序员来说,我们使用DBUnit进行测试可以有两种选择:通过直接编码方式进行测试或者与Ant结合.  
  编码方式
  DBUnit框架提供了一个基本的抽象测试用例类,叫做DatabaseTestCase,它是JUnit框架中的基础类TestCase的子类。如果我们使用这个类必须首先实现两个钩子方法(hook
methods):getConnection()和getDataSet().  
  方法getConnection()需要返回一个IDatabaseConnection类型的对象,这个对象是一个基于普通JDBC连接的包装类。例如,下面的代码段演示了在MySQL数据库环境下,IDatabaseConnection类型连接对象的创建方法。  
  
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类提供了两个fixture(我叫它固件,不知仁兄同意否?)方法来控制测试前和测试后的数据库状态。这两个方法就是:
  getSetUpOperation() 和 getTearDownOperation().  
  一种高效的实施方案就是让getSetUpOperation()方法执行REFRESH操作,通过这个操作,我们可以用种子文件中的数据去更新目标数据库里的数据。接下来,就是getTearDownOperation(),让他去执行一个NONE操作,也就是什么也不执行。
  
  protected DatabaseOperation getSetUpOperation()
  throws
  Exception {
  return DatabaseOperation.REFRESH;
  }
  
  protected DatabaseOperation getTearDownOperation()
  throws
  Exception {
  return DatabaseOperation.NONE;
  }
  

  还有一种有效的方法就是在getSetUpOperation()方法中执行CLEAN_INSERT操作,这样首先会将目标数据库中与我们提供的种子文件一致的数据删除,然后将我们提供的数据插入到数据库中。这个实施顺序保证了我们对数据库的精确控制。
代码样例
  在一个基于J2EE的人力资源系统中,我们很希望对某个数据操作周期实现测试自动化,这个操作周期包括职员的新增,检索,更新和删除。远程接口定义了下列的业务方法(为了简洁清楚,省略了方法中的throws子句).  
  //译者注:这里的EmployeeValueObject类型对象,译者认为是代表职员实体信息的对象。
  
  public void  createEmployee( EmployeeValueObject emplVo )
  
  public EmployeeValueObject  getEmployeeBySocialSecNum( String ssn )
  
  public void  updateEmployee( EmployeeValueObject emplVo )
  
  public void  deleteEmployee( EmployeeValueObject emplVo )
  

  测试getEmployeeBySocialSecNum()方法
  需要植入一条数据到目标数据库中,另外,测试deleteEmployee()方法和updateEmployee()方法时,同样也是在先前植入的这条记录的基础上进行。最后,测试类会首先利用createEmployee()方法创建一条记录,同时我们需要校验执行这个方法时,是否会有异常发生。  
  下面这个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,需要扩展DBUnit的基础类DatabaseTestCase并且必须提供对getConnection()和getDataSet()方法的实现,在getConnection()方法中将获得与EJB容器初始化时一样的数据库实例,getDataSet()方法负责读取上面提及的employee_hr_seed.xml文件的数据。  
  测试方法相当简单,因为DBUnit已经为我们处理了复杂的数据库生命周期任务。为了测试getEmployeeBySocialSecNum()方法,只需要简单的传递一个存在于种子文件中的社保代码号即可,比如
  "333-29-9999".  
  //译者注:EmployeeFacade 类型对象,译者认为是代表底层数据库数据的映射体
  
  
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()没有问题,我们只需简单的执行一下这个方法,然后校验一下看有没有异常抛出,另外,下一步我们要做的就是在这条新增的记录上进行查找操作,看是否可以找到刚创建的记录。
完整实例:
使用dbunit,可以帮助我们在测试中维护数据,也可以辅助我们的测试。
首先当然是下载dbunit, http://dbunit.sourceforge.net
我测试用的是 MYSQL 5.0 。
建立数据库:
create table test1( 
id int not null auto_increment, 
user_name varchar(50), 
primary key(id)) engine=innodb; 


保存数据的xml文件:
<dataset>  
    <test1 user_name="tom"/>                
    <test1 user_name="John"/>  
    <test1 user_name="Rose"/>     
</dataset>
<dataset>
    <test1 user_name="tom"/>             
    <test1 user_name="John"/>
    <test1 user_name="Rose"/>  
</dataset>

首先建立一个 JunitTest 的类:
public class Test2 extends TestCase {   
  
    protected void setUp() throws Exception { }   
  
    protected void tearDown() throws Exception { }   
     
}  

public class Test2 extends TestCase {

	protected void setUp() throws Exception {}

	protected void tearDown() throws Exception {}
  
}

我不喜欢继承dbunit的类,所以我们在JunitTest 的类里增加这个变量:
public class Test2 extends TestCase {   
  
    private IDatabaseTester databaseTester;   
  
    protected void setUp() throws Exception { }   
  
    protected void tearDown() throws Exception { }   
     
}  

public class Test2 extends TestCase {

	private IDatabaseTester databaseTester;

	protected void setUp() throws Exception {}

	protected void tearDown() throws Exception {}
  
}


然后,我们可以该写 setUp() 方法了,无非就是连接数据库,把数据倒入到表里。
protected void setUp() throws Exception {   
       
    databaseTester = new JdbcDatabaseTester("com.mysql.jdbc.Driver","jdbc:mysql://127.0.0.1:3306/test", "root", "123");   
       
    IDataSet dataSet = getDataSet();   
       
    databaseTester.setDataSet( dataSet );   
    databaseTester.onSetup();   
}   
  
protected IDataSet getDataSet() throws Exception   
   {   
       
       return new FlatXmlDataSet(new FileInputStream(new File("dataset.xml")));   
   }  
	protected void setUp() throws Exception {
		
		databaseTester = new JdbcDatabaseTester("com.mysql.jdbc.Driver","jdbc:mysql://127.0.0.1:3306/test", "root", "123");
		
		IDataSet dataSet = getDataSet();
		
		databaseTester.setDataSet( dataSet );
		databaseTester.onSetup();
	}

	protected IDataSet getDataSet() throws Exception
    {
		
        return new FlatXmlDataSet(new FileInputStream(new File("dataset.xml")));
    }

然后是 tearDown 方法
protected void tearDown() throws Exception   
   {   
  
    databaseTester.setTearDownOperation(DatabaseOperation.DELETE_ALL);   
       databaseTester.onTearDown();   
   }  

	protected void tearDown() throws Exception
    {

		databaseTester.setTearDownOperation(DatabaseOperation.DELETE_ALL);
        databaseTester.onTearDown();
    }


好了,准备工作完成了,下面开始写测试方法。

public void test1() throws Exception{   
       
    ITable test1Table = databaseTester.getDataSet().getTable("test1");   
    assertEquals(test1Table.getRowCount(), 3);   
       
}  

	public void test1() throws Exception{
		
		ITable test1Table = databaseTester.getDataSet().getTable("test1");
		assertEquals(test1Table.getRowCount(), 3);
		
	}

这个方法就是测试 test1 表有多少条记录
public void test2() throws Exception{   
       
    ITable test1Table = databaseTester.getConnection().createQueryTable("any_name",   
            "select user_name from test1 where user_name='tom'");   
       
    assertTrue(test1Table.getRowCount()==1);   
       
}  

	public void test2() throws Exception{
		
		ITable test1Table = databaseTester.getConnection().createQueryTable("any_name",
				"select user_name from test1 where user_name='tom'");
		
		assertTrue(test1Table.getRowCount()==1);
		
	}


这个方法测试是否有 user_name='tom' 这个记录. 那么自动增加字段怎么办呢? 只要在 tearDown 里增加
databaseTester.setTearDownOperation(DatabaseOperation.TRUNCATE_TABLE);  

databaseTester.setTearDownOperation(DatabaseOperation.TRUNCATE_TABLE);
就可以了!

分享到:
评论

相关推荐

    dbunit-2.2.3-prj.rar_DbUnit 2.2_dbunit_单元测试

    在实际使用DbUnit进行单元测试时,开发者通常会按照以下步骤进行: 1. 创建数据集:定义XML数据集文件,该文件描述了测试所需的数据状态。 2. 配置DbUnit:在测试类中设置数据库连接信息和数据集路径。 3. 填充...

    dbunit单元测试

    DBUnit 是一个强大的开源工具,专门用于数据库的单元测试,它是JUnit框架的一个扩展,使得开发者在进行测试时能够更有效地管理和控制数据库的状态。这个工具的主要目的是确保测试的隔离性和可重复性,使得每次测试都...

    通向架构师的道路(第二十五天)SSH的单元测试与dbunit的整合.docx

    今天,我们将讨论如何使用JUnit和DbUnit进行单元测试,并将其与SSH整合。 SSH单元测试 在SSH项目中,单元测试是一个非常重要的步骤,它可以确保代码的正确性和可靠性。使用JUnit,我们可以编写单元测试用例来测试...

    用DbUnit进行SqlMap单元测试

    在本文中,我们看到一个使用 DbUnit 进行 SqlMap(iBATIS 的一部分,用于映射 SQL 查询到 Java 代码)DAO 单元测试的例子。SqlMap 是一个流行的 ORM(Object-Relational Mapping)框架,用于处理 SQL 数据库与 Java ...

    基于DbUnit的单元测试框架设计

    DbUnit是一款开源的数据库功能测试框架,使用它可以对数据库的基本操作进行白盒单元测试,对输入输出进行校验,从而保证数据的有效性。DbUnit使用XML文件提供测试数据,为测试人员增加了测试难度,降低了单元测试效率。...

    Junit+dbunit单元测试jar包

    在下载的"Junit,dbunit单元测试jar包"中,可能包含了`Junit`和`dbunit`的库文件,以及其他必要的依赖,如数据库驱动。这些库文件可以被添加到项目的类路径中,以便在项目中使用它们提供的功能。开发者应当根据具体的...

    单元测试JUnit4和DbUnit

    DbUnit则是一个专门用于数据库单元测试的工具,它允许开发者在测试前后对数据库的状态进行操作,如填充测试数据、清理数据等,以保证每次测试都在一致的环境中进行。 首先,了解JUnit4的基础知识至关重要。JUnit4...

    主题:在Spring中结合Dbunit对Dao进行集成单元测试

    在Spring框架中,进行Dao层的集成单元测试时,常常会结合Dbunit工具来实现数据库相关的测试。Dbunit是一款强大的Java库,它扩展了JUnit,为数据库提供了结构化数据的导入和导出,使得测试更加方便和可靠。下面将详细...

    unitils整合dbunit利用excel进行单元测试

    unitils整合dbunit利用excel进行单元测试 包含mock以及整合spring进行测试

    使用dbunit测试数据库

    总的来说,DbUnit 是一个强大的工具,它简化了对数据库驱动的应用进行单元测试的过程。通过控制数据库的状态,开发者可以更准确地测试代码逻辑,而不用担心外部数据的影响。在实际的开发流程中,利用 DbUnit 可以...

    DBUnit与H2内存数据库结合(单元测试)

    DBUnit与H2内存数据库结合是进行单元测试的一种高效方法,尤其在开发Java应用程序时,它可以帮助开发者确保数据层的功能正确性。这篇文章将详细介绍如何利用DBUnit和H2内存数据库来构建单元测试环境。 首先,DBUnit...

    数据库操作的单元测试

    DBunit是一个开源工具,专为需要对数据库操作进行单元测试的场景设计。在软件开发过程中,特别是当应用程序与数据库紧密耦合时,进行有效的单元测试变得非常具有挑战性。DBunit通过为测试用例提供一种机制来控制和...

    通向架构师的道路(第二十五天)SSH的单元测试与dbunit的整合的例子

    本篇将深入探讨如何在SSH项目中进行单元测试,并结合dbunit进行数据准备。 首先,Spring框架提供了强大的测试支持。Spring Test模块允许开发者对Spring应用程序上下文进行便捷的加载和测试,可以对单个Bean或整个...

    junit单元测试jar包集

    这里提到的四个文件是Java开发中常用的单元测试框架和库,分别是JUnit、DBUnit、Unitils和Mockito。让我们逐一深入探讨它们的功能和使用方法。 **JUnit** 是Java领域中最广泛使用的单元测试框架,这里的`junit-4.11...

Global site tag (gtag.js) - Google Analytics