DBUnit入门(转自:http://www.blogjava.net/liuzheng/articles/190128.html)

本文将为您介绍DbUnit,一个专门针对数据库测试的JUnit扩展。通过DbUnit,您可以在测试之前备份数据库,然后在测试过程中给对象数据库植入所需数据,并在测试完成后恢复到原始状态。DbUnit简化了数据库单元测试的过程,提高了测试效率。
相信做过单元测试的人都会对JUnit非常的熟悉了,今天要介绍的DbUnit(http://dbunit.sourceforge.net/)则是专 门针对数据库测试的对JUnit的一个扩展,它可以将测试对象数据库置于一个测试轮回之间的状态。鉴于目前国内介绍DbUnit的系统教程比较少见,本文 将分从理论和实例两个方面带你领略DbUnit的精彩世界。

DbUnit设计理念
熟悉单元测试的开发人员都知道,在对数据库进行单元测试时候,通常采用的方案有运用模拟对象(mock objects)和stubs两种。通过隔离关联的数据库访问类,比如JDBC的相关操作类,来达到对数据库操作的模拟测试。然而某些特殊的系统,比如利 用了EJB的CMP(container-managed persistence)的系统,数据库的访问对象是在最底层而且很隐蔽的,那么这两种解决方案对这些系统就显得力不从心了。

DBUnit的设计理念就是在测试之前,备份数据库,然后给对象数据库植入我们需要的准备数据,最后,在测试完毕后,读入备份数据库,回溯到测试前的状态;
而且又因为DBUnit是对JUnit的一种扩展,开发人员可以通过创建测试用例代码,在这些测试用例的生命周期内来对数据库的操作结果进行比较。

DbUnit测试基本概念和流程
基于DbUnit 的测试的主要接口是IDataSet。IDataSet代表一个或多个表的数据。
可以将数据库模式的全部内容表示为单个IDataSet 实例。这些表本身由Itable 实例来表示。
IDataSet 的实现有很多,每一个都对应一个不同的数据源或加载机制。最常用的几种 IDataSet实现为:
FlatXmlDataSet:数据的简单平面文件 XML 表示
QueryDataSet:用 SQL 查询获得的数据
DatabaseDataSet:数据库表本身内容的一种表示
XlsDataSet :数据的excel表示

一般而言,使用DbUnit进行单元测试的流程如下:
1 根据业务,做好测试用的准备数据和预想结果数据,通常准备成xml格式文件。
2 在setUp()方法里边备份数据库中的关联表。
3 在setUp()方法里边读入准备数据。
4 对测试类的对应测试方法进行实装:执行对象方法,把数据库的实际执行结果和预想结果进行比较。
5 在tearDown()方法里边,把数据库还原到测试前状态。

DbUnit开发实例
下面通过一个实例来说明DbUnit的实际运用。

实例准备
比如有一个学生表[student],结构如下:


id char(4) pk 学号
name char(50) 姓名
sex char(1) 性别
birthday date 出生日期

准备数据如下:

id name sex birthday
0001 翁仔 m 1979-12-31
0002 王翠花 f 1982-08-09
测试对象类为StudentOpe.java,里边有2个方法:
findStudent(String id) :根据主键id找记录
addStudent(Student student) :添加一条记录

在测试addStudent方法时候,我们准备添加如下一条数据

id name sex birthday
0088 王耳朵 m 1982-01-01

那么在执行该方法后,数据库的student表里的数据是这样的:

id name sex birthday
0001 翁仔 m 1979-12-31
0002 王翠花 f 1982-08-09
0088 王耳朵 m 1982-01-01

然后我们说明如何对这2个方法进行单元测试。

实例展开
1 把准备数据和预想数据转换成xml文件
student_pre.xml


<?xml version='1.0' encoding="gb2312"?> <dataset> <student id="0001" name="翁仔" sex="m" birthday="1979-12-31"/> <student id="0002" name="王翠花" sex="f" birthday="1982-08-09"/> </dataset>



student_exp.xml

<?xml version='1.0' encoding="gb2312"?> <dataset> <student id="0001" name="翁仔" sex="m" birthday="1979-12-31"/> <student id="0002" name="王翠花" sex="f" birthday="1982-08-09"/> <student id="0088" name="王耳朵" sex="m" birthday="1982-01-01"/> </dataset>


2 实装setUp方法,详细见代码注释。


protected void setUp() { IDatabaseConnection connection =null; try{ super.setUp(); //本例使用postgresql数据库 Class.forName("org.postgresql.Driver"); //连接DB Connection conn=DriverManager.getConnection("jdbc:postgresql:testdb.test","postgres","postgres"); //获得DB连接 connection =new DatabaseConnection(conn); //对数据库中的操作对象表student进行备份 QueryDataSet backupDataSet = new QueryDataSet(connection); backupDataSet.addTable("student"); file=File.createTempFile("student_back",".xml");//备份文件 FlatXmlDataSet.write(backupDataSet,new FileOutputStream(file)); //准备数据的读入 IDataSet dataSet = new FlatXmlDataSet( new FileInputStream("student_pre.xml")); DatabaseOperation.CLEAN_INSERT.execute(connection,dataSet); }catch(Exception e){ e.printStackTrace(); }finally{ try{ if(connection!=null) connection.close(); }catch(SQLException e){} } }


3 实装测试方法,详细见代码注释。

*检索类方法,可以利用assertEquals()方法,拿表的字段进行比较。

// findStudent public void testFindStudent() throws Exception{ //执行findStudent方法 StudentOpe studentOpe=new StudentOpe(); Student result = studentOpe.findStudent("0001"); //预想结果和实际结果的比较 assertEquals("翁仔",result.getName()); assertEquals("m",result.getSex()); assertEquals("1979-12-31",result.getBirthDay()); }



*更新,添加,删除等方法,可以利用Assertion.assertEquals()方法,拿表的整体来比较。

public void testAddStudent() throws Exception{ //执行addStudent方法 StudentOpe studentOpe=new StudentOpe(); //被追加的记录 Student newStudent = new Student("0088","王耳朵","m","1982-01-01"); //执行追加方法 Student result = studentOpe.addStudent(newStudent); //预想结果和实际结果的比较 IDatabaseConnection connection=null; try{ //预期结果取得 IDataSet expectedDataSet = new FlatXmlDataSet(new FileInputStream("student_exp.xml")); ITable expectedTable = expectedDataSet.getTable("student"); //实际结果取得 Connection conn=getConnection(); connection =new DatabaseConnection(conn); IDataSet databaseDataSet = connection.createDataSet(); ITable actualTable = databaseDataSet.getTable("student"); //比较 Assertion.assertEquals(expectedTable, actualTable); }finally{ if(connection!=null) connection.close(); } }



*如果在整体比较表的时候,有个别字段不需要比较,可以用DefaultColumnFilter.excludedColumnsTable()方法,
将指定字段给排除在比较范围之外。比如上例中不需要比较birthday这个字段的话,那么可以如下代码所示进行处理:

ITable filteredExpectedTable = DefaultColumnFilter.excludedColumnsTable(expectedTable, new String[]{"birthday"}); ITable filteredActualTable = DefaultColumnFilter.excludedColumnsTable(actualTable,new String[]{"birthday"}); Assertion.assertEquals(filteredExpectedTable, filteredActualTable);



4 在tearDown()方法里边,把数据库还原到测试前状态

protected void tearDown() throws Exception{ IDatabaseConnection connection =null; try{ super.tearDown(); Connection conn=getConnection(); connection =new DatabaseConnection(conn); IDataSet dataSet = new FlatXmlDataSet(file); DatabaseOperation.CLEAN_INSERT.execute(connection,dataSet); }catch(Exception e){ e.printStackTrace(); }finally{ try{ if(connection!=null) connection.close(); }catch(SQLException e){} } }



最后
无疑,使用DbUnit能够极大的提高数据库测试的效率,希望通过本文能够让您掌握这一数据库测试的利器
"original_store_code","created_datetime","pos_number","pos_sequence_number","amount","update_datetime","update_function_id","update_person_id","update_count" "123456",2024-07-08 19:58:43,"1","0555",99999999999,2025-10-09 17:59:57,"1","1",0 报错:org.dbunit.dataset.DataSetException: error producing dataset for table 't_journal_amount_information_s3' at org.dbunit.dataset.csv.CsvProducer.produce(CsvProducer.java:95) at org.dbunit.dataset.CachedDataSet.<init>(CachedDataSet.java:88) at org.dbunit.dataset.CachedDataSet.<init>(CachedDataSet.java:73) at org.dbunit.dataset.csv.CsvDataSet.<init>(CsvDataSet.java:43) at jp.co.sej.ssc.sh.common.CsvDataSetLoader.createDataSet(CsvDataSetLoader.java:23) at com.github.springtestdbunit.dataset.AbstractDataSetLoader.loadDataSet(AbstractDataSetLoader.java:54) at com.github.springtestdbunit.DbUnitRunner.loadDataset(DbUnitRunner.java:209) at com.github.springtestdbunit.DbUnitRunner.loadDataSets(DbUnitRunner.java:192) at com.github.springtestdbunit.DbUnitRunner.setupOrTeardown(DbUnitRunner.java:173) at com.github.springtestdbunit.DbUnitRunner.beforeTestMethod(DbUnitRunner.java:75) at com.github.springtestdbunit.DbUnitTestExecutionListener.beforeTestMethod(DbUnitTestExecutionListener.java:185) at org.springframework.test.context.TestContextManager.beforeTestMethod(TestContextManager.java:320) at org.springframework.test.context.junit.jupiter.SpringExtension.beforeEach(SpringExtension.java:240) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625) at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762) at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:276) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:276) at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) Caused by: org.dbunit.dataset.csv.CsvParserException: Expected 9 columns on line 2, got 1. Offending line: at org.dbunit.dataset.csv.CsvParserImpl.collectExpectedNumberOfColumns(CsvParserImpl.java:196) at org.dbunit.dataset.csv.CsvParserImpl.parseTheData(CsvParserImpl.java:159) at org.dbunit.dataset.csv.CsvParserImpl.parse(CsvParserImpl.java:124) at org.dbunit.dataset.csv.CsvParserImpl.parse(CsvParserImpl.java:99) at org.dbunit.dataset.csv.CsvProducer.produceFromFile(CsvProducer.java:112) at org.dbunit.dataset.csv.CsvProducer.produce(CsvProducer.java:93) ... 43 more
10-10
org.dbunit.dataset.DataSetException: org.postgresql.util.PSQLException: ERROR: current transaction is aborted, commands ignored until end of transaction block at org.dbunit.database.DatabaseDataSet.getTable(DatabaseDataSet.java:342) at com.github.springtestdbunit.assertion.NonStrictDatabaseAssertion.assertEquals(NonStrictDatabaseAssertion.java:47) at com.github.springtestdbunit.DbUnitRunner.verifyExpected(DbUnitRunner.java:154) at com.github.springtestdbunit.DbUnitRunner.verifyExpected(DbUnitRunner.java:120) at com.github.springtestdbunit.DbUnitRunner.afterTestMethod(DbUnitRunner.java:86) at com.github.springtestdbunit.DbUnitTestExecutionListener.afterTestMethod(DbUnitTestExecutionListener.java:190) at org.springframework.test.context.TestContextManager.afterTestMethod(TestContextManager.java:487) at org.springframework.test.context.junit.jupiter.SpringExtension.afterEach(SpringExtension.java:278) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) Caused by: org.postgresql.util.PSQLException: ERROR: current transaction is aborted, commands ignored until end of transaction block at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2733) at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2420) at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:372) at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:517) at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:434) at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:356) at org.postgresql.jdbc.PgStatement.executeCachedSql(PgStatement.java:341) at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:317) at org.postgresql.jdbc.PgStatement.executeQuery(PgStatement.java:266) at java.base/java.lang.reflect.Method.invoke(Method.java:569) at org.apache.tomcat.jdbc.pool.StatementFacade$StatementProxy.invoke(StatementFacade.java:123) at jdk.proxy2/jdk.proxy2.$Proxy217.executeQuery(Unknown Source) at org.dbunit.database.AbstractResultSetTable.<init>(AbstractResultSetTable.java:110) at org.dbunit.database.ForwardOnlyResultSetTable.<init>(ForwardOnlyResultSetTable.java:59) at org.dbunit.database.CachedResultSetTableFactory.createTable(CachedResultSetTableFactory.java:60) at org.dbunit.database.DatabaseDataSet.getTable(DatabaseDataSet.java:338) ... 9 more Caused by: org.postgresql.util.PSQLException: ERROR: numeric field overflow 詳細: A field with precision 10, scale 0 must round to an absolute value less than 10^10. at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2733) at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2420) at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:372) at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:517) at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:434) at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:194) at org.postgresql.jdbc.PgPreparedStatement.execute(PgPreparedStatement.java:180) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:569) at org.apache.tomcat.jdbc.pool.StatementFacade$StatementProxy.invoke(StatementFacade.java:123) at jdk.proxy2/jdk.proxy2.$Proxy218.execute(Unknown Source) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:569) at org.apache.ibatis.logging.jdbc.PreparedStatementLogger.invoke(PreparedStatementLogger.java:58) at jdk.proxy3/jdk.proxy3.$Proxy220.execute(Unknown Source) at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:48) at org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler.java:75) at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:50) at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117) at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76) at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:197) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:569) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:425) at jdk.proxy2/jdk.proxy2.$Proxy189.update(Unknown Source) at org.mybatis.spring.SqlSessionTemplate.update(SqlSessionTemplate.java:288) at com.nec.jp.rpf.zwb.framework.datastoreaccess.FWSingleDao.update(FWSingleDao.java:52) at jp.co.sej.ssc.pu.spua3546.logic.Spua3546Logic.execute(Spua3546Logic.java:166) at jp.co.sej.ssc.pu.spua3546.Spua3546LogicTest.test_1_1_12(Spua3546LogicTest.java:94) at java.base/java.lang.reflect.Method.invoke(Method.java:569) ... 2 more
最新发布
10-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值