我正在使用HSQLDB进行数据层集成测试,这很棒。但是,我发现我的外键约束妨碍了我的测试。例如,要在一个表上测试一个简单的选择,我必须将虚拟数据插入另外五个表中。这让我想扔东西。
我在整个模型代码中都有JPA注释,并已配置Hibernate以在配置中重新创建模式(hbm2ddl.create-drop)。在生成表时,连接被正确解释为外键约束。
我想要的是:
如果它有用,我正在使用Spring来自动测试这些测试。有问题的测试继承自AbstractTransactionalJUnit4SpringContextTests。
你怎么看?可以这样做吗?答案 0 :(得分:9)
您可以使用以下指令停用FK约束:
SET REFERENTIAL_INTEGRITY FALSE;
您可以在测试方法之前通过JDBC Statement
执行它(并在之后将其设置回TRUE
)。
答案 1 :(得分:8)
我在尝试使用平面xml数据集测试DAO时遇到了同样的问题。 Config是DBunit + HSQLDB 2.2.8 + JUnit4 + Spring + JPA->一共导致
java.sql.SQLIntegrityConstraintViolationException: integrity constraint violation: foreign key no parent; FK13EE6CE6F09A6AAC table: ****
我通过实现扩展AbstractTestExecutionListener
的侦听器找到了一个很好的解决方法。您可以指定在每次测试之前要采取的操作类型,在我们的示例中,禁用外键约束。
注意:语法可能因使用的HSQLDB版本而异。
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();
}
}
然后,您只需要在测试中已经存在的集合中添加此侦听器:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"applicationContext-test.xml"})
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DataSetTestExecutionListener.class, ForeignKeyDisabling.class})
答案 2 :(得分:2)
基于这个主题的灵感,我为这个问题创建了一些更强大的解决方案。关键是,我在运行测试时非常喜欢约束,所有其他解决方案都让它被禁用。此代码仅在数据集导入期间禁用它们,然后重新启用它们。并且可以轻松扩展以支持另一个数据库引擎:
import com.github.springtestdbunit.DbUnitTestExecutionListener;
import org.apache.log4j.Logger;
import org.dbunit.database.DatabaseDataSourceConnection;
import org.dbunit.database.IDatabaseConnection;
import org.springframework.test.context.TestContext;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Class DisableForeignKeysDbUnitTestExecutionListener
* Simple wrapper class around DbUnitTestExecutionListener, which - for the time of importing the database -
* disables Foreign Key Constraints checks.
* This class can be extended by simply overriding toggleForeignKeysConstraintsForDbEngine(Connection, String, boolean);
* subclasses should always call super-implementation for default case.
*/
public class DisableForeignKeysDbUnitTestExecutionListener
extends DbUnitTestExecutionListener
{
private static final Logger logger = Logger.getLogger(DisableForeignKeysDbUnitTestExecutionListener.class);
private Connection cachedDbConnection;
@Override
public void beforeTestMethod(TestContext testContext)
throws Exception
{
this.toggleForeignKeysConstraints(testContext, false);
super.beforeTestMethod(testContext);
this.toggleForeignKeysConstraints(testContext, true);
}
/**
* Method should perform query to disable foreign keys constraints or return false,
* if it is not able to perform such query (e.g. unknown database engine)
*
* @param connection Database connection
* @param dbProductName Name of the database product (as reported by connection metadata)
* @param enabled Expected state of foreign keys after the call
*
* @return True, if there was suitable statement for specified engine, otherwise false
*
* @throws SQLException
*/
protected boolean toggleForeignKeysConstraintsForDbEngine(Connection connection, String dbProductName, boolean enabled)
throws SQLException
{
switch (dbProductName)
{
case "HSQL Database Engine":
connection.prepareStatement("SET DATABASE REFERENTIAL INTEGRITY " + (enabled ? "TRUE" : "FALSE"))
.execute();
return (true);
}
return (false);
}
private void toggleForeignKeysConstraints(TestContext testContext, boolean enabled)
{
try
{
Connection connection = this.getDatabaseConnection(testContext);
String databaseProductName = connection.getMetaData().getDatabaseProductName();
if (!this.toggleForeignKeysConstraintsForDbEngine(connection, databaseProductName, enabled))
{
throw new IllegalStateException("Unknown database engine '" + databaseProductName +
"'. Unable to toggle foreign keys constraints.");
}
}
catch (Throwable throwable)
{
logger.error("Unable to toggle Foreign keys constraints: " + throwable.getLocalizedMessage());
}
}
synchronized private Connection getDatabaseConnection(TestContext testContext)
throws SQLException
{
if (this.cachedDbConnection == null)
{
DataSource dataSource = testContext.getApplicationContext().getBean(DataSource.class);
if (dataSource == null)
{
throw new IllegalStateException("Unable to obtain DataSource from ApplicationContext. " +
"Foreign constraints will not be disabled.");
}
IDatabaseConnection dsConnection = new DatabaseDataSourceConnection(dataSource);
this.cachedDbConnection = dsConnection.getConnection();
}
return (this.cachedDbConnection);
}
}
答案 3 :(得分:0)
我会考虑花一些时间来创建几个灯具,可能还有DBUnit,你可以插入@Before。
BTW,AbstractTransactionalJUnit4Test在Spring 3.0中已弃用
答案 4 :(得分:0)
快速做到:
SET REFERENTIAL_INTEGRITY FALSE;
到您的测试资源目录中的import.sql
文件中。
问题解决得很快很好:)
以下是有关import.sql
http://christopherlakey.com/articles/import-sql.html