When writing unit tests on the data access layer, you may encounter many problems. Some problems can be easily solved by Google, while others can only be solved by yourself. Here are some typical problems and solutions.
Let's first explain the testing framework Spring Test + springtestdbunit + dbunit used.
1. Let's talk about a low-level problem first.
Spring uses the <JDBC: Embedded-database> label to support memory data, as shown in the following figure:
<jdbc:embeded-database id="dataSource" type="HSQL">
However, at startup, an error is always prompted:
Caused by: Org. XML. sax. saxparseexception; linenumber: 31; columnnumber: 57; cvc-complex-type.2.4.c: wildcard matching is comprehensive, but the declaration of the element 'jdbc: Embedded-database' cannot be found.
At com.sun.org. Apache. xerces. Internal. util. errorhandlerwrapper. createsaxparseexception (errorhandlerwrapper. Java: 198)
At com.sun.org. Apache. xerces. Internal. util. errorhandlerwrapper. Error (errorhandlerwrapper. Java: 134)
At com.sun.org. Apache. xerces. Internal. impl. xmlerrorreporter. reporterror (xmlerrorreporter. Java: 437)
......
The tag has been modified many times, and the document and DTD have been read many times. No problems have been found. Finally, I accidentally saw a problem with the label declaration in the context file header:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/tx/spring-jdbc-3.2.xsd">
After careful reading, when I copied the Declaration from Tx, I changed the final TX to JDBC, but forgot to change the Tx in the path to JDBC. After the modification, the system starts normally. All. If a student encounters a similar problem, check the header first.
Ii. deletion failed due to foreign key Association.
At the beginning of the write test, each use case runs independently, but once it runs together, the following exception occurs:
Tests run: 5, failures: 0, errors: 3, skipped: 0, time elapsed: 0.879 sec <failure! -In COM. noyaxe. NSO. Service. deviceservicetest
Testinitializedforbindedspaceforcebind (COM. noyaxe. NSO. Service. deviceservicetest) Time elapsed: 0.309 sec <error!
Java. SQL. sqlintegrityconstraintviolationexception: Integrity Constraint Violation: foreign key no action; fk_l6idvk78b2tlu8no6edj0g6u8 table: custom_table_column_space_type
At org. HSQLDB. JDBC. util. sqlexception (unknown source)
At org. HSQLDB. JDBC. util. sqlexception (unknown source)
At org. HSQLDB. JDBC. jdbcstatement. fetchresult (unknown source)
......
......
Caused by: org. HSQLDB. hsqlexception: Integrity Constraint Violation: foreign key no action; fk_l6idvk78b2tlu8no6edj0g6u8 table: custom_table_column_space_type
At org. HSQLDB. Error (unknown source)
At org. HSQLDB. statementdml. extends mreferentialactions (unknown source)
At org. HSQLDB. statementdml. Delete (unknown source)
At org.hsqldb.statementdml.exe cutedeletestatement (unknown source)
At org. HSQLDB. statementdml. getresult (unknown source)
At org.hsqldb.statementdmql.exe cute (unknown source)
At org.hsqldb.session.exe cutecompiledstatement (unknown source)
At org.hsqldb.session.exe cutedirectstatement (unknown source)
At org.hsqldb.session.exe cute (unknown source)
At org. HSQLDB. JDBC. jdbcstatement. fetchresult (unknown source)
......
Check the exception information, which is caused by cascade of Foreign keys when the record is deleted. Changing cascade settings in an object class does not work. Finally, I found a solution on stackoverflow: compile a class, inherit abstracttestexecutionlistener, and cancel cascading dependency in beforetestclass. The details are as follows:
import org.dbunit.database.DatabaseDataSourceConnection;import org.dbunit.database.IDatabaseConnection;import org.springframework.test.context.TestContext;import org.springframework.test.context.support.AbstractTestExecutionListener;import javax.sql.DataSource;public class ForeignKeyDisabling extends AbstractTestExecutionListener { @Override public void beforeTestClass(TestContext testContext) throws Exception { IDatabaseConnection dbConn = new DatabaseDataSourceConnection( testContext.getApplicationContext().getBean(DataSource.class) ); dbConn.getConnection().prepareStatement("SET DATABASE REFERENTIAL INTEGRITY FALSE").execute(); }}
Add the new listener to the annotation of the test class:
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:applicationContext-test.xml")@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, TransactionDbUnitTestExecutionListener.class, ForeignKeyDisabling.class})
Reference: http://stackoverflow.com/questions/2685274/tdd-with-hsqldb-removing-foreign-keys
3. Warnings caused by property_datatype_factory
The following warning information is always displayed when Jenkins is created:
Warn getdatatypefactory, potential problem found: the configured data type factory 'class Org. dbunit. dataset. datatype. defaultdatatypefactory 'might cause problems with the current database 'hsql Database Engine '(e.g. some datatypes may not be supported properly ). in rare cases you might see this message because the list of supported database products is incomplete (list = [Derby]). if so please request a Java-class update via the forums. if you are using your own idatatypefactory extending defaultypefactory, ensure that you override getvaliddbproducts () to specify the supported database products.
The default datatypefactory may cause problems. We recommend that you set this attribute value. The solution is also obvious: it is to set the value of the property_datatype_factory attribute of the database connection. This attribute cannot be set in before, beforeclass, or custom executionlistener.
Then we can only find the location where the exception is thrown, push it forward, and gradually find the location where the connection is obtained. Finally, we found that the connection was obtained in dbunittestexecutionlistener. preparedatabaseconnection without further processing. Therefore, the previous settings did not work. It seems that you can only rewrite the source code to achieve the goal.
Go directly to the source code:
Customtransactiondbunittestexecutionlistener class: completely copy dbunittestexecutionlistener, just add a code. Note that the package path of this class is consistent with that of dbunittestexecutionlistener.
private void prepareDatabaseConnection(TestContext testContext, String databaseConnectionBeanName) throws Exception { Object databaseConnection = testContext.getApplicationContext().getBean(databaseConnectionBeanName); if (databaseConnection instanceof DataSource) { databaseConnection = DatabaseDataSourceConnectionFactoryBean.newConnection((DataSource) databaseConnection); } Assert.isInstanceOf(IDatabaseConnection.class, databaseConnection); ((IDatabaseConnection)databaseConnection).getConfig().setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new HsqldbDataTypeFactory()); testContext.setAttribute(CONNECTION_ATTRIBUTE, databaseConnection); }
Green is the code that actually works.
However, this class cannot be used directly. Instead, it is called through the chain of transactiondbunittestexecutionlistener, and transactiondbunittestexecutionlistener cannot be changed. You can only create a custom transactiondbunittestexecutionlistener class and customtransactiondbunitexecutionlistener:
public class CustomTransactionDbUnitTestExecutionListener extends TestExecutionListenerChain { private static final Class<?>[] CHAIN = { TransactionalTestExecutionListener.class, CustomDbUnitTestExecutionListener.class }; @Override protected Class<?>[] getChain() { return CHAIN; }}
The annotation of the test class should also be modified:
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:applicationContext-test.xml")@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, CustomTransactionDbUnitTestExecutionListener.class, ForeignKeyDisabling.class})
4. Problems Caused by @ transactional labels
As described in spring-dbunit-test, @ transactional can be used to ensure data cleansing. Simple to use. You only need to add @ transactional to the annotation above,
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:applicationContext-test.xml")@Transactional@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, CustomTransactionDbUnitTestExecutionListener.class, ForeignKeyDisabling.class})
However, an exception occurs during running:
Org. springframework. transaction. transactionsystemexception: cocould not roll back JPA transaction; Nested exception is javax. Persistence. persistenceexception: Unexpected error when rollbacking
At org. springframework. Orm. JPA. jpatransactionmanager. dorollback (jpatransactionmanager. Java: 544)
At org. springframework. transaction. Support. abstractplatformtransactionmanager. processrollback (abstractplatformtransactionmanager. Java: 846)
At org. springframework. transaction. Support. abstractplatformtransactionmanager. rollback (abstractplatformtransactionmanager. Java: 823)
At org. springframework. Test. Context. transaction. transactionaltestexecutionlistener $ transactioncontext. endtransaction (transactionaltestexecutionlistener. Java: 588)
At org. springframework. Test. Context. transaction. transactionaltestexecutionlistener. endtransaction (transactionaltestexecutionlistener. Java: 297)
At org. springframework. Test. Context. transaction. transactionaltestexecutionlistener. aftertestmethod (transactionaltestexecutionlistener. Java: 192)
......
Caused by: javax. Persistence. persistenceexception: Unexpected error when rollbacking
At org. hibernate. EJB. transactionimpl. rollback (transactionimpl. Java: 109)
At org. springframework. Orm. JPA. jpatransactionmanager. dorollback (jpatransactionmanager. Java: 540)
... 32 more
Caused by: org. hibernate. transactionexception: rollback failed
At org. hibernate. Engine. transaction. SPI. abstracttransactionimpl. rollback (abstracttransactionimpl. Java: 215)
At org. hibernate. EJB. transactionimpl. rollback (transactionimpl. Java: 106)
... 33 more
Caused by: org. hibernate. transactionexception: Unable to rollback against JDBC connection
At org. hibernate. Engine. transaction. Internal. JDBC. jdbctransaction. dorollback (jdbctransaction. Java: 167)
At org. hibernate. Engine. transaction. SPI. abstracttransactionimpl. rollback (abstracttransactionimpl. Java: 209)
... 34 more
Caused by: Java. SQL. sqlnontransientconnectionexception: connection exception: connection does not exist
At org. HSQLDB. JDBC. util. sqlexception (unknown source)
At org. HSQLDB. JDBC. util. sqlexception (unknown source)
......
... 35 more
Caused by: org. HSQLDB. hsqlexception: connection exception: connection does not exist
At org. HSQLDB. Error (unknown source)
At org. HSQLDB. Error (unknown source)
... 40 more
Finally, we can check the source code and find that customdbunittestexecutionlistener will be executed before transactionaltestexecutionlistener. The former closes the database connection after execution, and the latter has encountered an exception that does not exist during rollback.
The solution is simple. Modify customtransactionaldbunittestexecutionlistener:
private static final Class<?>[] CHAIN = {CustomDbUnitTestExecutionListener.class, TransactionalTestExecutionListener.class};
That is to say, the two elements of the array change the location.