数据库单元测试框架?
Posted
技术标签:
【中文标题】数据库单元测试框架?【英文标题】:DB Unit Testing framework? 【发布时间】:2011-10-19 22:37:45 【问题描述】:在我的项目中,我使用了 spring、jpa 和 PostgreSQL DB, 我在数据库中有很多表,我需要对所有表进行单元测试。
是否有任何框架可以在每次测试完成后回滚所有事务,以便每次测试都有新的/相同的数据库数据进行测试。这样,在所有测试执行之后,数据库模式的数据就会保持原样。
对此有何建议?
我对 DBUnit 有所了解,但我需要为每个测试的每个输入数据编写 .xml 文件,并且需要在 setup() 中插入数据并在 tearDown() 中清除/删除数据,但不需要对我来说似乎是更好的策略。
感谢任何建议。 谢谢。
【问题讨论】:
【参考方案1】:正如@Ryan 所指出的......应该咨询Testing section of the Spring Reference manual。
一些启动技巧...
我们已经使用 Spring 的 AbstractTransactionalJUnit4SpringContextTests
处理了这个问题。
例如,我们定义一个抽象超类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:WebContent/WEB-INF/testconfig/test-web-application-config.xml")
@TransactionConfiguration()
@Transactional
public abstract class OurAbstractTransactionalSpringContextTest extends AbstractTransactionalJUnit4SpringContextTests
然后需要额外上下文的各个子类被定义为:
@ContextConfiguration("classpath:path/to/config/ConfigForTestCase.xml")
public class TestOurFunction extends OurAbstractTransactionalSpringContextTest
@Test
public void testOurMethod()
注意:
-
并非所有测试类都需要额外的上下文,请跳过特定子类上的
@ContextConfiguration
。
我们通过 ant 执行并在 junit
任务上使用 forkmode="perBatch"
属性。这确保了所有测试都使用相同的上下文配置运行(避免为每个测试重新加载 Spring 上下文)。您可以使用@DirtiesContext
指示在方法/类之后应刷新上下文。
用@Test
注释标记每个方法。 Spring 框架不会使用 Junit 的 public void testXXX()
约定来选择方法。
【讨论】:
【参考方案2】:是否有任何框架可以在每次测试完成后回滚所有事务,以便每次测试都有新的/相同的数据库数据进行测试。这样,在所有测试执行之后,数据库模式的数据就会保持原样。
来自当天早些时候发布的my other answer,是的,这可以使用 DbUnit。 (根据您的编辑,您不需要这个;我的答案的后续部分说明了我使用 DbUnit 的原因以及何时不使用它)。
以下代码 sn-p 演示了如何执行每个测试的设置:
@Before
public void setUp() throws Exception
logger.info("Performing the setup of test ", testName.getMethodName());
IDatabaseConnection connection = null;
try
connection = getConnection();
IDataSet dataSet = getDataSet();
//The following line cleans up all DbUnit recognized tables and inserts and test data before every test.
DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
finally
// Closes the connection as the persistence layer gets it's connection from elsewhere
connection.close();
private IDatabaseConnection getConnection() throws Exception
@SuppressWarnings( "rawtypes", "unused" )
Class driverClass = Class.forName("org.apache.derby.jdbc.ClientDriver");
Connection jdbcConnection = DriverManager.getConnection(jdbcURL, "XXX",
"YYY");
IDatabaseConnection databaseConnection = new DatabaseConnection(jdbcConnection);
return databaseConnection;
private IDataSet getDataSet() throws Exception
ClassLoader classLoader = this.getClass().getClassLoader();
return new FlatXmlDataSetBuilder().build(classLoader.getResourceAsStream("database-test-setup.xml"));
database-test-setup.xml
文件包含将为每次测试插入数据库的数据。在setup
方法中使用DatabaseOperation.CLEAN_INSERT
可确保文件中指定的所有表都将被清除(通过删除所有行),然后将指定数据插入到测试数据文件中。
避免使用 DbUnit
我使用上述方法专门在每次测试开始之前清除序列,因为应用程序使用 JPA 提供程序在单独的事务中更新序列。如果您的应用程序没有做任何类似的事情,那么您只需在 setup()
方法中启动事务并在测试后在拆卸时发出回滚。如果我的应用程序不使用序列(并且如果我不想重置它们),那么我的设置例程将非常简单:
@Before
public void setUp() throws Exception
logger.info("Performing the setup of test ", testName.getMethodName());
// emf is created in the @BeforeClass annotated method
em = emf.createEntityManager();
// Starts the transaction before every test
em.getTransaction.begin();
@After
public void tearDown() throws Exception
logger.info("Performing the teardown of test ", testName.getMethodName());
if (em != null)
// Rolls back the transaction after every test
em.getTransaction().rollback();
em.close();
另外,我在 Maven 中使用 dbdeploy,但这主要是为了使测试数据库与版本化数据模型保持同步。
【讨论】:
感谢您的回复,它真的很有帮助,但我认为第一种方法(DbUnit)存在数据插入的开销,因为它在 setup() 期间为每个测试完成,所以例如可以有很多测试我们需要一个/两个表中的数据,但每次 setup() 都会为所有表插入数据。 我认为第二种方法(事务)不会将数据保存在数据库中(它不会对数据进行实际持久性),因此数据库不会抛出约束违规异常,并且似乎不适用于所有场景.如果我错了,请告诉我。 @SmartSolution,您始终可以为每个灯具拥有单独的 XML 文件。当然,如果夹具中的每个测试只需要一部分数据并且这是开销,那么我建议使用Testcase Class per Fixture pattern。关于约束违反和引发异常的主题,据我所知,当您刷新 PersistenceContext 而不是在 COMMIT 时将引发持久性异常。 @SmartSolution,如果您不刷新持久性上下文,那么请考虑更改您的存储库/DAO 类来这样做。如果您有一组单元测试将在普通 Java SE 环境中创建持久性上下文并测试您的 DAO/存储库行为,则可以避免这种情况 - 这不会测试事务传播等。【参考方案3】:Spring's test framework 正是为你做的。
【讨论】:
这是正确的,但不是很具体。请参阅 Jacob 的详细说明。【参考方案4】:我会按照以下方式处理它。
当项目处于测试模式时。我使用引导数据来测试使用dbdeploy
可以断言的固定数据。并直接使用dao
来测试您的应用程序的DAO 和DB 层。
希望对你有帮助
更新
例如,您的系统中有一个名为Person
的实体,现在您可以在此测试的是基本的 CRUD 操作。
要回滚你可以标记的事务
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
所以它会回滚数据库的东西
【讨论】:
通过这种方式,由于数据未在数据库中提交,因此不会测试数据的持久性,并且可能不会抛出一些违反约束等异常,因此这种与事务相关的方法似乎不是正确的策略适用于所有场景。 你的意思是说,不提交trasaction就会抛出约束冲突异常?如果是的话,我真的不知道这个,你能给出一些想法吗? @JigarJoshi 让我们continue this discussion in chat以上是关于数据库单元测试框架?的主要内容,如果未能解决你的问题,请参考以下文章