解决Spring单元测试中因外键关联导致的失败integrity constraint violation:foreign key no action
2020年9月8日大约 3 分钟
前言
Spring单元测试中因外键关联导致的失败,每个用例单独运行都没有问题,可是一起运行,就出现下面的异常错误信息:integrity constraint violation: foreign key no action。
出现错误时使用的环境
- spring:3.2.16.RELEASE
- unitils:3.4.2 (unitils-core、unitils-spring、unitils-dbunit、unitils-inject)
- hsqldb:2.3.2
- hibernate3:3.6.5.Final
单元测试类继承UnitilsJUnit4
,以使用unitils进行测试。查看UnitilsJUnit4的Runner为@RunWith(UnitilsJUnit4TestClassRunner.class)
@ContextConfiguration( {"classpath*:applicationContext.xml" }) //① 初始化Spring容器
@DataSet("/data.xls")
public class IServiceImplTest extends UnitilsJUnit4{
@Autowired
IService service;
@Test
public void testMethod() {
// do something
}
}
当进行单元测试时,每个用例单独运行都没有问题,可是一起运行,就出现下面的异常错误信息:integrity constraint violation: foreign key no action
。详细错误栈见下:
Caused by: org.unitils.core.UnitilsException: Error while executing DataSetLoadStrategy
at org.unitils.dbunit.datasetloadstrategy.impl.BaseDataSetLoadStrategy.execute(BaseDataSetLoadStrategy.java:48)
at org.unitils.dbunit.DbUnitModule.insertDataSet(DbUnitModule.java:342)
at org.unitils.dbunit.DbUnitModule.insertDataSet(DbUnitModule.java:268)
at org.unitils.dbunit.DbUnitModule.insertDataSet(DbUnitModule.java:187)
... 17 more
Caused by: java.sql.BatchUpdateException: integrity constraint violation: foreign key no action; FKD5D67893BB8A6726 table: T_TEST_TABLENAME
at org.hsqldb.jdbc.JDBCStatement.executeBatch(Unknown Source)
at org.apache.commons.dbcp.DelegatingStatement.executeBatch(DelegatingStatement.java:297)
at org.dbunit.database.statement.BatchStatement.executeBatch(BatchStatement.java:59)
at org.dbunit.operation.DeleteAllOperation.execute(DeleteAllOperation.java:126)
at org.dbunit.operation.CompositeOperation.execute(CompositeOperation.java:79)
at org.unitils.dbunit.datasetloadstrategy.impl.CleanInsertLoadStrategy.doExecute(CleanInsertLoadStrategy.java:45)
at org.unitils.dbunit.datasetloadstrategy.impl.BaseDataSetLoadStrategy.execute(BaseDataSetLoadStrategy.java:44)
... 20 more
问题解决
很明显,这是由于批量更新时外键级联导致的问题,解决思路主要是要在执行单元测试前取消级联依赖,其实根据hsql官方文档这个级联依赖应该默认是false
,是否是Hibernate将其设为True还有待证实。
编写一个类,继承
AbstractTestExecutionListener
实现自定义Listener,在beforeTestClass
中取消级联依赖。具体如下: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 { testContext.getApplicationContext() .getAutowireCapableBeanFactory() .autowireBean(this); IDatabaseConnection dbConn = new DatabaseDataSourceConnection( testContext.getApplicationContext().getBean(DataSource.class) ); dbConn.getConnection().prepareStatement("SET DATABASE REFERENTIAL INTEGRITY FALSE").execute(); } }
然后使用
@TestExecutionListeners
注解将这个自定义Listener作用到测试类上。注意:这里不能使用unitils进行测试了(unitils使用@RunWith(UnitilsJUnit4TestClassRunner.class)),这里需要使用@RunWith(SpringJUnit4ClassRunner.class)
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; import org.unitils.dbunit.annotation.DataSet; import java.util.ArrayList; import java.util.List; import static org.junit.Assert.*; @ContextConfiguration( {"classpath*:applicationContext.xml" }) //① 初始化Spring容器 @DataSet("/data.xls") @RunWith(SpringJUnit4ClassRunner.class) // 不能使用UnitilsJUnit4TestClassRunner @TestExecutionListeners(listeners = { ForeignKeyDisabling.class, //自定义的Listener DependencyInjectionTestExecutionListener.class // Spring自带的对测试类中的依赖进行注入的Listener }) public class IServiceImplTest{ @Autowired IService service; @Test public void testMethod() { // do something } }
再运行单元测试就没有问题了。