在做
Java
企业程序的时候,不可避免地要和外部资源打交道,比如数据库,
Http
请求等。对于这些外部资源的处理,我们可采取的操作或者是直接处理或者是模拟处理。当我们使用
Webwork
,
Spring
,
Hibernate
等框架时,我们要测试的并不仅仅是
Java
代码,我们还要测试依赖于这些框架的配置文件等等。因此,对于数据持久化的测试,
Mock
方法是行不通的,我们需要真实地测试数据库操作。对于持久化测试来说,重要的是创造出已知的“干净的”的准备数据。如果我们在测试一个持久化方法前不能确定数据库到底存着什么数据,我们只能通过反复地查看数据库数据来验证测试方法的正确性了(这就是我和大多数人以前使用的最“直接”的方法)。现在就让我们使用
DbUnit
,来更好的更自动化的测试持久化操作吧!
先介绍一下
DbUnit
。
DbUnit
是一个
JUnit
扩展,适用于数据驱动的程序。使用
DbUnit
,可以在测试运行期间将数据库的数据处于已知状态,这样在测试时可以方便地写出测试断言,也能自动地完成对数据持久化方法的测试。在使用上,
DbUnit
也很简单,
它提供了大量的类对与数据库相关的操作进行了抽象和封装,大多数情况下你只需要使用少量简单的
API
。
下面我通过一个实际的小例子,介绍一下如何使用
DbUnit
。我也是刚刚使用上了
DbUnit
,经验上不是很丰富,如果文中有不对的地方,也欢迎指正。这个例子很简单,我将较为详细地说明如何使用
Hibernate
和
DbUnit
进行测试。
测试第一步,准备数据集。操作的数据表就是如下的
Account
表(使用的数据库为Mysql):
create
table
Account(
id
bigint
not
null
auto_increment,
name
varchar
(
50
)
not
null
,
primary
key
(id)
)
character
set
gbk;
至于
Account
类,映射文件等这里就不给出了。AccountDAO
接口很简单,只有两个方法:
public
interface
AccountDAO {
void
insert(Account a);
List
<
Account
>
findAll();
}
实现类如下:
public
class
AccountHibernateDAO
implements
AccountDAO{
public
void
insert(Account a){
Session s
=
HibernateSessionFactory.getSession();
s.save(a);
s.close();
}
public
List
<
Account
>
findAll(){
Session s
=
HibernateSessionFactory.getSession();
List
<
Account
>
l
=
(List
<
Account
>
)s.createCriteria(Account.
class
).list();
s.close();
return
l;
}
}
在测试前,要准备出数据表中要装入的数据(也就是数据集),这里给出与Account表对应的数据集文件
Accout.xml
内容如下:
<?
xml version='1.0' encoding='UTF-8'
?>
<
dataset
>
<
Account
id
="1"
name
="kafka"
/>
<
Account
id
="2"
name
="0102"
/>
</
dataset
>
数据集就是一个
xml
文件,
<dataset>
中的每个节点对应的就是一条表数据记录(一个
dataset文件可以对应多个数据表记录
)。这里的
<Account>
节点对应的就是
Account
表,属性就是表中的字段,属性值就是字段值了。在做测试时,数据集中的内容可以手动敲进去,也可以通过工具将数据库中的数据导出来。
对于数据集的详细信息,可参考
http://dbunit.sourceforge.net/components.html#FlatXmlDataSet
。
测试第二步,扩展
DBTestCase
。
DBTestCase
是继承自
JUnit
的类,扩展它需要实现
getDataSet()
来提供数据集。
另外,你也可以根据需要扩展继承于
DBTestCase
的子类
JdbcBasedDBTestCase
,
DataSourceBasedDBTestCas
,
JndiBasedDBTestCase
。下面是继承于
DBTestCase
的
AccountHibernateDAO
的测试类
AccountHibernateDAOTest
:
package
hibernatesample.dao.impl;
import
hibernatesample.domain.Account;
import
hibernatesample.util.HibernateSessionFactory;
import
java.io.File;
import
java.io.InputStream;
import
java.util.List;
import
org.dbunit.Assertion;
import
org.dbunit.DBTestCase;
import
org.dbunit.PropertiesBasedJdbcDatabaseTester;
import
org.dbunit.dataset.IDataSet;
import
org.dbunit.dataset.ITable;
import
org.dbunit.dataset.xml.FlatXmlDataSet;
import
org.dbunit.operation.DatabaseOperation;
public
class
AccountHibernateDAOTest
extends
DBTestCase {
private
AccountHibernateDAO accountDAO;
public
AccountHibernateDAOTest(){
System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_DRIVER_CLASS, HibernateSessionFactory.getDriverClass());
System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_CONNECTION_URL, HibernateSessionFactory.getConnectionURL());
System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_USERNAME,HibernateSessionFactory.getUsername());
System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_PASSWORD,HibernateSessionFactory.getPassword());
}
@Override
protected
IDataSet getDataSet()
throws
Exception {
String path
=
"
hibernatesample
"
+
File.separator
+
"
dao
"
+
File.separator
+
"
dataset
"
+
File.separator
+
"
Account.xml
"
;
InputStream in
=
this
.getClass().getClassLoader().getResourceAsStream(path);
return
new
FlatXmlDataSet(in);
}
@Override
protected
DatabaseOperation getSetUpOperation()
throws
Exception {
return
DatabaseOperation.CLEAN_INSERT;
}
@Override
protected
DatabaseOperation getTearDownOperation()
throws
Exception {
return
DatabaseOperation.NONE;
}
protected
void
setUp()
throws
Exception {
super
.setUp();
accountDAO
=
new
AccountHibernateDAO();
}
public
void
testInsert() {
Account a
=
new
Account();
a.setName(
"
aa
"
);
accountDAO.insert(a);
List
<
Account
>
l
=
accountDAO.findAll();
assertEquals(
3
, l.size());
Account b
=
l.get(
2
);
assertEquals(
"
aa
"
, b.getName());
}
public
void
testFindAll() {
List
<
Account
>
l
=
accountDAO.findAll();
assertEquals(
2
, l.size());
Account a
=
l.get(
0
);
assertEquals(
new
Long(
1
), a.getId());
assertEquals(
"
kafka
"
, a.getName());
Account b
=
l.get(
1
);
assertEquals(
new
Long(
2
), b.getId());
assertEquals(
"
0102
"
, b.getName());
}
public
void
testDataset()
throws
Exception {
IDataSet databaseDataSet
=
getConnection().createDataSet();
ITable actualTable
=
databaseDataSet.getTable(
"
Account
"
);
IDataSet expectedDataSet
=
getDataSet();
ITable expectedTable
=
expectedDataSet.getTable(
"
Account
"
);
Assertion.assertEquals(expectedTable, actualTable);
}
}
上面的DBTestCase
依赖于
IDatabaseTester
接口完成工作,而
PropertiesBasedJdbcDatabaseTester
就是其使用的默认实现,
AccountHibernateDAOTest
构造函数的作用是完成数据库连接参数的设置。
protected
IDataSet getDataSet()
实现了装载数据集到
IDataSet
中。
getSetUpOperation
和
getTearDownOperation
是可选的方法,返回的
DatabaseOperation
为
DBTestCase
在
SetUp
和
TearDown
中将执行的操作,
getSetUpOperation
默认的操作为
DatabaseOperation.CLEAN_INSERT
,也就是先清空数据表中的数据再插入数据集中的数据到数据表中。getTearDownOperation
默认的操作为
DatabaseOperation.NONE
,就是什么也不处理。可选的操作还有几个,可参考文档进行设置,但默认的设置是最通用的了。
testDataset
()只是测试数据集中的数据和装载到数据库中数据是否一致。
通过上面的设置,我们就可以测试dao方法的正确性了,而我们要做的只是准备dataset文件及使用少量的DbUnit
API(可以将这些操作写到一个抽象类中,测试类都继承自这个抽象类)。下一篇将介绍整合Spring,Hibernate和DbUnit进行测试。