The path to continuous integration-problems encountered in data access layer Unit Testing

Source: Internet
Author: User

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.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.