当前位置:   article > 正文

使用DBUnit集成Spring简化测试_com.github.springtestdbunit.annotation.databaseset

com.github.springtestdbunit.annotation.databasesetup

    在上一篇博文使用DBUnit做单元测试中,谈到了使用DBUnit进行数据准备做单测试,可以方便我们的数据准备以及后面的执行结果的验证,简化了我们做单元测试的准备工作,不过其中有一个不足就是,单元测试的代码过多的和DBUnit进行了耦合,并且其中还使用到了SQL,这个感觉非常不好,对于我们使用习惯了Spring进行解耦的人来说,总想使它简化一点,下面这个例子就是使用Spring集成DBUnit,进一步简化我们使用DBUnit做数据准备的工作。

    我所做的就是将数据准备写成Annotation的方式,这样可以方便进行数据准备,写了两个Annotation,一个是用于Method的,一个是用于Class,用于Method的数据准备可以被事务管理,即数据在跑完测试后,就会被rollback,可是用于Class的却不能够在跑完测试后被回滚,于是就在找这里的问题。可是我却在一篇BLOG里面发现已经有现成的集成于spring test的dbunit annotation了,https://github.com/springtestdbunit/spring-test-dbunit/,测试了一下,我实现的功能它都已经实现了,并且把expected result也有一个实现了的Annotation ExpectedDatabase,那我的代码就直接扔掉了。只是这个ExpectedDatabase Annotation有一点不足的就是,不能够对结果数据进行排序,因为有的数据在插入到数据库中后,顺序就和Expected的结果集就会不一样了,这点我会在后面有说明,如何弥补这样的情况。以下是一个详细的实例,测试JAVA是放在名为com.dbunit.test的package中:

    1、一些关键性的依赖

  1. <dependency>
  2. <artifactId>spring-jdbc</artifactId>
  3. <groupId>org.springframework</groupId>
  4. <scope>runtime</scope>
  5. </dependency>
  6. <dependency>
  7. <artifactId>spring-core</artifactId>
  8. <groupId>org.springframework</groupId>
  9. <scope>compile</scope>
  10. </dependency>
  11. <dependency>
  12. <artifactId>spring-beans</artifactId>
  13. <groupId>org.springframework</groupId>
  14. <scope>compile</scope>
  15. </dependency>
  16. <dependency>
  17. <artifactId>spring-context</artifactId>
  18. <groupId>org.springframework</groupId>
  19. <scope>compile</scope>
  20. </dependency>
  21. <dependency>
  22. <artifactId>spring-context-support</artifactId>
  23. <groupId>org.springframework</groupId>
  24. <scope>compile</scope>
  25. </dependency>
  26. <dependency>
  27. <artifactId>spring-test</artifactId>
  28. <groupId>org.springframework</groupId>
  29. <scope>test</scope>
  30. </dependency>
  31. <dependency>
  32. <artifactId>dbunit</artifactId>
  33. <groupId>org.dbunit</groupId>
  34. <scope>test</scope>
  35. </dependency>
  36. <dependency>
  37. <groupId>jotm</groupId>
  38. <artifactId>jotm</artifactId>
  39. <scope>test</scope>
  40. </dependency>
  41. <dependency>
  42. <groupId>com.experlog</groupId>
  43. <artifactId>xapool</artifactId>
  44. <scope>test</scope>
  45. </dependency>
  46. <dependency>
  47. <groupId>junit</groupId>
  48. <artifactId>junit</artifactId>
  49. <scope>test</scope>
  50. </dependency>

    注:这里没有写spring-test-dbunit的依赖,那是因为在我环境我使用的是spring-test-dbunit的源码。

    2、建表语句

  1. create table YouTableName_1(
  2. filed_1 int,
  3. filed_2 varchar2(50),
  4. filed_3 varchar2(50)
  5. )
    3、准备数据的DBUnit数据文件:MyTest.xml

  1. <?xml version='1.0' encoding='UTF-8'?>
  2. <dataset>
  3. <YouTableName_1 Filed_1="1" Filed_2="f2" Filed_3="f3"/>
  4. <YouTableName_1 Filed_1="2" Filed_2="f2_1" Filed_3="f3_1"/>
  5. </dataset>

    4、期待的结果DBUnit数据文件:MyTest_Result.xml

  1. <?xml version='1.0' encoding='UTF-8'?>
  2. <dataset>
  3. <YouTableName_1 Filed_1="1" Filed_2="a" Filed_3="a1"/>
  4. <YouTableName_1 Filed_1="2" Filed_2="b" Filed_3="b1"/>
  5. </dataset>
    5、Spring的配置文件:spring.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
  4. xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
  5. xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
  6. xsi:schemaLocation="
  7. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
  8. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
  9. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
  10. http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
  11. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
  12. <bean id="jotm" class="org.objectweb.jotm.Current" />
  13. <bean id="dataSource" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
  14. <property name="dataSource">
  15. <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
  16. <property name="transactionManager" ref="jotm" />
  17. <property name="driverName" value="oracle.jdbc.driver.OracleDriver" />
  18. <!-- oracle.jdbc.driver.OracleDriver com.p6spy.engine.spy.P6SpyDriver -->
  19. <property name="url" value="jdbc:oracle:thin:@1.1.1.1:1521:dbschema" />
  20. <property name="user" value="username" />
  21. <property name="password" value="password" />
  22. </bean>
  23. </property>
  24. <property name="user" value="username" />
  25. <property name="password" value="password" />
  26. </bean>
  27. <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
  28. <property name="userTransaction" ref="jotm" />
  29. </bean>
  30. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  31. <property name="dataSource" ref="dataSource">
  32. </property>
  33. </bean>
  34. <!-- Scan the package and register the bean into container -->
  35. <context:component-scan base-package="com.dbunit.test" />
  36. </beans>
    6、用于测试的JAVA代码

    UpdateTest.java

  1. package com.dbunit.test;
  2. import java.sql.SQLException;
  3. import javax.inject.Inject;
  4. import javax.inject.Named;
  5. import org.springframework.jdbc.core.JdbcTemplate;
  6. @Named("UpdateTest")
  7. public class UpdateTest {
  8. @Inject
  9. JdbcTemplate jdbcTemplate;
  10. public void updateFiled() throws SQLException {
  11. jdbcTemplate.execute("update YouTableName_1 set filed_2='a',filed_3='a1' where filed_1=1");
  12. jdbcTemplate.execute("update YouTableName_1 set filed_2='b',filed_3='b1' where filed_1=2");
  13. }
  14. }

     MyTest.java
  1. package com.dbunit.test;
  2. import java.io.IOException;
  3. import java.sql.SQLException;
  4. import javax.inject.Inject;
  5. import junit.framework.Assert;
  6. import org.junit.Test;
  7. import com.github.springtestdbunit.annotation.DatabaseSetup;
  8. import com.github.springtestdbunit.annotation.ExpectedDatabase;
  9. import com.github.springtestdbunit.assertion.DatabaseAssertionMode;
  10. public class MyTest extends BasedTestCase {
  11. @Inject
  12. UpdateTest updateTest;
  13. @Test
  14. @DatabaseSetup({ "classpath:/MyTest.xml" })
  15. @ExpectedDatabase(assertionMode=DatabaseAssertionMode.NON_STRICT,value="classpath:/MyTest_Result.xml")
  16. public void testSend() throws IOException, SQLException {
  17. try {
  18. updateTest.updateFiled();
  19. } catch (Exception e) {
  20. e.printStackTrace();
  21. Assert.assertTrue(false);
  22. }
  23. }
  24. }

     BasedTestCase.java

  1. package com.dbunit.test;
  2. import org.junit.runner.RunWith;
  3. import org.springframework.test.context.ContextConfiguration;
  4. import org.springframework.test.context.TestExecutionListeners;
  5. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  6. import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
  7. import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
  8. import org.springframework.transaction.annotation.Transactional;
  9. import com.github.springtestdbunit.TransactionDbUnitTestExecutionListener;
  10. @RunWith(SpringJUnit4ClassRunner.class)
  11. @ContextConfiguration(locations = { "classpath:/spring.xml" })
  12. @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class, TransactionDbUnitTestExecutionListener.class })
  13. @Transactional
  14. public abstract class BasedTestCase {
  15. }

    这就是这个简单的测试示例所需要的全部文件,也可以运行成功了。

    不过如果其它的测试,虽然执行结果正确,但是JUnit的执行结果却是错误的,这是因为我们准备的数据在被插入到数据库中后,记录的顺序可能就被打乱了,此时的执行结果的记录顺序就会和我们期待的结果就会不一样。这个时候的解决方法就是将查询执行结果的时候,加上某个字段的order by,就可以得到我们期望的结果。

    此时我们将MyTest.java BaseTestCase.java修改成如下这样的:

    MyTest.java

  1. package com.dbunit.test;
  2. import java.io.IOException;
  3. import java.sql.SQLException;
  4. import javax.inject.Inject;
  5. import junit.framework.Assert;
  6. import org.dbunit.dataset.ReplacementDataSet;
  7. import org.junit.Test;
  8. import com.github.springtestdbunit.annotation.DatabaseSetup;
  9. import com.github.springtestdbunit.annotation.ExpectedDatabase;
  10. import com.github.springtestdbunit.assertion.DatabaseAssertionMode;
  11. public class MyTest extends BasedTestCase {
  12. @Inject
  13. UpdateTest updateTest;
  14. @Test
  15. @DatabaseSetup({ "classpath:/MyTest.xml" })
  16. //The comparation can be kept, it's maybe correct
  17. @ExpectedDatabase(assertionMode=DatabaseAssertionMode.NON_STRICT,value="classpath:/MyTest_Result.xml")
  18. public void testSend() throws IOException, SQLException {
  19. try {
  20. updateTest.updateFiled();
  21. // get result data set by result xml file
  22. ReplacementDataSet dataload_result = createDataSet(Thread.currentThread().getContextClassLoader().getResourceAsStream("MyTest_Result.xml"));
  23. // compare the data which get from database and the expected result file
  24. assertDataSet("YouTableName_1", "select filed_1,filed_2,filed_3 from YouTableName_1 order by filed_1", dataload_result);
  25. } catch (Exception e) {
  26. e.printStackTrace();
  27. Assert.assertTrue(false);
  28. }
  29. }
  30. }

    

    BaseTestCase.java

  1. package com.dbunit.test;
  2. import java.io.InputStream;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import java.util.Map;
  6. import java.util.TreeMap;
  7. import javax.inject.Inject;
  8. import javax.sql.DataSource;
  9. import junit.framework.Assert;
  10. import org.dbunit.Assertion;
  11. import org.dbunit.database.DatabaseConnection;
  12. import org.dbunit.database.IDatabaseConnection;
  13. import org.dbunit.database.QueryDataSet;
  14. import org.dbunit.dataset.Column;
  15. import org.dbunit.dataset.IDataSet;
  16. import org.dbunit.dataset.ITable;
  17. import org.dbunit.dataset.ReplacementDataSet;
  18. import org.dbunit.dataset.filter.DefaultColumnFilter;
  19. import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
  20. import org.junit.runner.RunWith;
  21. import org.springframework.test.context.ContextConfiguration;
  22. import org.springframework.test.context.TestExecutionListeners;
  23. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  24. import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
  25. import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
  26. import org.springframework.transaction.annotation.Transactional;
  27. import com.github.springtestdbunit.TransactionDbUnitTestExecutionListener;
  28. @RunWith(SpringJUnit4ClassRunner.class)
  29. @ContextConfiguration(locations = { "classpath:/spring.xml" })
  30. @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class, TransactionDbUnitTestExecutionListener.class })
  31. @Transactional
  32. public abstract class BasedTestCase {
  33. @Inject
  34. DataSource dataSource;
  35. /**
  36. * This is used to assert the data from table and the expected data set. If all of the them has the same records, then the assert is true.
  37. *
  38. * @param tableName
  39. * @param sql
  40. * @param expectedDataSet
  41. * @throws Exception
  42. */
  43. protected void assertDataSet(String tableName, String sql, IDataSet expectedDataSet) throws Exception {
  44. if (dataSource == null) {
  45. throw new RuntimeException("There must be dataSource in the spring config file");
  46. }
  47. IDatabaseConnection iconn = new DatabaseConnection(dataSource.getConnection());
  48. printDataAsXml(iconn, tableName, sql);
  49. QueryDataSet loadedDataSet = new QueryDataSet(iconn);
  50. loadedDataSet.addTable(tableName, sql);
  51. ITable table1 = loadedDataSet.getTable(tableName);
  52. ITable table2 = expectedDataSet.getTable(tableName);
  53. Assert.assertEquals(table2.getRowCount(), table1.getRowCount());
  54. DefaultColumnFilter.includedColumnsTable(table1, table2.getTableMetaData().getColumns());
  55. Assertion.assertEquals(table2, table1);
  56. }
  57. /**
  58. * Create the data set by input stream which read from the dbunit xml data file.
  59. *
  60. * @param is
  61. * @return
  62. * @throws Exception
  63. */
  64. protected ReplacementDataSet createDataSet(InputStream is) throws Exception {
  65. return new ReplacementDataSet(new FlatXmlDataSetBuilder().build(is));
  66. }
  67. /**
  68. * Convert the data in the ITable to List
  69. *
  70. * @param table
  71. * @return
  72. * @throws Exception
  73. */
  74. private List<Map<?, ?>> getDataFromTable(ITable table) throws Exception {
  75. List<Map<?, ?>> ret = new ArrayList<Map<?, ?>>();
  76. int count_table = table.getRowCount();
  77. if (count_table > 0) {
  78. Column[] columns = table.getTableMetaData().getColumns();
  79. for (int i = 0; i < count_table; i++) {
  80. Map<String, Object> map = new TreeMap<String, Object>();
  81. for (Column column : columns) {
  82. map.put(column.getColumnName().toUpperCase(), table.getValue(i, column.getColumnName()));
  83. }
  84. ret.add(map);
  85. }
  86. }
  87. return ret;
  88. }
  89. /**
  90. * Get data by the SQL and table name, then convert the data in the ITable to List
  91. *
  92. * @param iconn
  93. * @param tableName
  94. * @param sql
  95. * @return
  96. * @throws Exception
  97. */
  98. private List<Map<?, ?>> getTableDataFromSql(IDatabaseConnection iconn, String tableName, String sql) throws Exception {
  99. ITable table = iconn.createQueryTable(tableName, sql);
  100. return getDataFromTable(table);
  101. }
  102. /**
  103. * Get data by the SQL and table name, then convert the data in the ITable to List. And the print the data as xml data format.
  104. *
  105. * @param iconn
  106. * @param tableName
  107. * @param sql
  108. * @throws Exception
  109. */
  110. private void printDataAsXml(IDatabaseConnection iconn, String tableName, String sql) throws Exception {
  111. List<Map<?, ?>> datas = getTableDataFromSql(iconn, tableName, sql);
  112. StringBuffer sb;
  113. for (Map<?, ?> data : datas) {
  114. sb = new StringBuffer();
  115. sb.append("<" + tableName.toUpperCase() + " ");
  116. for (Object o : data.keySet()) {
  117. sb.append(o + "=\"" + data.get(o) + "\" ");
  118. }
  119. sb.append("/>");
  120. System.out.println(sb.toString());
  121. }
  122. }
  123. }

这里我们验证了两次结果,通常执行结果是正确的,如果执行结果失败了,可以把ExpectedDatabase Annotation给注释掉,再试试,当然你的expected结果文件一定要和你期的执行结果相同了。



声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/376923
推荐阅读
相关标签
  

闽ICP备14008679号