公司介绍 产品展示 业务支持 解决方案 文档资料
网站导航 访客留言 技术论坛
     
  产品资料
  业界动态
     
  JAVA技术


首  页 > 文档资料 > JAVA技术  
 
  扩展DbUnit

现在不少应用都是关联数据库的,这个测试带来了一定的麻烦。在测试过程中,数据经常会被修改,而测试却往往需要数据的状态一定,这样可以给测试断言语句或其他测试提供一个基准。让我们举两个实际的例子来说吧。我们在测试Struts的ChangePasswordAction类时,这个Action负责完成更改用户密码的功能,如果测试成功的话,将会更改某一用户的登录密码,如果在测试完毕后没有恢复至测试前状态,那么原先使用旧密码进行登陆Action的测试将无法成功(red bar),在实际的操作方面,如果没有通知相关测试人员,他们仍然使用旧密码,势必无法登陆系统。如果你的一个业务逻辑非常复杂,牵涉到多个表操作,包括添加、更新、删除等等,在测试这个逻辑时,如果在测试过程中出现了异常,如果你没有控制好事务的话,可能就会出现数据的不连续这种情况,你需要回到数据库的操作界面下,删除这些不连续数据(Dirty Data)以保证数据的准确性,这是一个非常烦人的事情。这些出现在测试中数据状态不定性,给以后的测试带来很大的麻烦,同时你的自动化测试将无法进行。

我们希望有这样的功能,在测试某一TestCase前将相关的数据备份起来,然后进行测试,在测试完毕后将数据在恢复至测试前状态,这样就不会对其他的TestCase造成影响。这里我们创建这样的一个接口:


/**
* 数据处理操作:备份、恢复
*/
public interface DBOperations
{
/**
* 备份数据库中相关的表
*
* @param tableNames 数据库中的表名
*/
public void backupTables(String[] tableNames) throws Exception;

/**
* 恢复数据至前一备份状态,这里执行删除后添加操作
*/
public void restoreTables() throws Exception;

/**
* 恢复数据至前一备份状态,这里执行更新操作
*
* @throws Exception
*/
public void restoreTablesWithUpdate() throws Exception;

/**
* 获取DbUnit对应的数据库连接
*
* @return 数据库连接
*/
public Connection getDataBaseConnection() throws Exception;

}

其中backupTables(String[] tableNames)完成对指定表的备份,restoreTables()恢复至前一备份状态。这样我们在测试过程中只需把握好备份和恢复操作就可以保证不会随意更改数据库中的数据。
下面我们就需要实现这个接口,这里我们需要使用DbUnit。DbUnit是对JUnit的扩展,主要是将测试中的数据信息维护在一指定的状态,这样可以避免一个测试修改了数据导致接下来的测试失败的情况。下面我们就看一下如何使用DbUnit和具体的测试结合起来为测试服务,下面我们会以三个例子来说明:普通测试,Struts的Action测试和Spring的Bean测试。使用DbUnit实现机理很简单,在测试进行前,我们调用DbUnit将数据备份起来,这里以缓存形式保存,在测试结束前,在将缓存的数据更新到数据库中。下面让我们看看普通测试类的实现:


/**
* 包含数据备份和恢复操作的测试基类
*
* @noinspection JUnitTestCaseInProductSource,JUnitTestCaseWithNoTests
*/
public abstract class DatabBaseTest extends TestCase implements DBOperations
{
private DatabaseConnection unitConnection; //DbUnit的数据库连接
private CachedDataSet dataSet; //存储临时数据

/**
* 初始化DbUnit的数据库连接
*/
protected void setUp() throws Exception
{
super.setUp();
initDbUnitConnection();
}

/**
* 初始化DbUnit的数据库连接
*
* @throws Exception 数据库连接出错
*/
private void initDbUnitConnection() throws Exception
{
Connection conn = getDefinedConnection();
if (conn != null)
unitConnection = new DatabaseConnection(conn);
}

/**
* 获取数据库的连接
*/
public abstract Connection getDefinedConnection();

/**
* 备份数据库中相关的表
*
* @param tableNames 数据库中的表名
*/
public void backupTables(String[] tableNames) throws Exception
{
dataSet = new CachedDataSet(unitConnection.createDataSet(tableNames));
}

/**
* 恢复数据至前一备份状态,这里执行删除后添加操作
*/
public void restoreTables() throws Exception
{
DatabaseOperation.CLEAN_INSERT.execute(unitConnection, dataSet);
//noinspection AssignmentToNull
dataSet = null;
}

/**
* 恢复数据至前一备份状态,这里执行更新操作
*
* @throws Exception
*/
public void restoreTablesWithUpdate() throws Exception
{
DatabaseOperation.CLEAN_INSERT.execute(unitConnection, dataSet);
//noinspection AssignmentToNull
dataSet = null;
}

/**
* 获取DbUnit对应的数据库连接
*
* @return 数据库连接
* @throws Exception 获取连接错误
*/
public Connection getDataBaseConnection() throws Exception
{
return unitConnection.getConnection();
}

/**
* 清除DbUnit的数据库连接操作
*
* @throws Exception 数据库连接释放出错
*/
protected void tearDown() throws Exception
{
super.tearDown();
if (unitConnection != null)
unitConnection.close();
}
}

在具体的项目测试中,我们只需提供一个数据库连接,在setUp方法中只需执行备份表操作,在tearDown方法只需恢复数据表操作即可。注意:请不要在测试方法中调用备份和恢复操作语句,因为在测试中可能出现异常,不能保证恢复操作会被调用。同时要保证调用父类的setUp()和tearDown()方法。下面就是一个测试样例:


public class DemoTest extends DataBaseTest
{
public Connection getDefinedConnection()
{
Connection conn = null;
try
{
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
conn = DriverManager.getConnection("jdbc:mysql://localhost/test", "root", "");
} catch (SQLException e)
{
e.printStackTrace();
}
return conn;
}

protected void setUp() throws Exception
{
super.setUp();
backupTables(new String[]{"acl_user"});
}

/**
* The fixture clean up called after every test method.
*/
protected void tearDown() throws Exception
{
restoreTables();
super.tearDown();
}

public void testDbOperation() throws Exception
{

}
}

这样我们就完成一个TestCase数据状态维护的操作,这样在这个TestCase执行完毕后可以保证数据恢复到先前状态,不会对其他TestCase造成影响。

在测试Struts的Action时,我们只需给DbUnit测试类提供DataSource,在测试Spring的Bean,只需提供DataSource bean的名称即可,这些都和框架进行了结合,保证测试的便捷性。关于Struts和Spring的DbUnit测试类,请参考附件中的源码。

总结:数据状态维护非常必要,测试都是基于一定的数据状态进行的,所以保持状态,不干扰其他测试类的数据非常必要。数据状态被维护好啦,自动化测试就成为可能,尤其在涉及到数据库测试方面,如果在需要人为去干涉和调整数据,那么必要会造成测试效率的降低,持续集成测试等将无法进行下去,规划一下各个TestCase的数据状态,会为后来的测试带来非常好便利。

相关下载:jet_dbunit.zip

参考资料:

http://dbunit.sourceforge.net

http://cruisecontrol.sourceforge.net

http://www.junit.org