我真的很喜欢Spring,因此我倾向于充分利用它的功能。 但是,在其哲学的某些黑暗角落,我倾向于不同意其某些假设。 一种这样的假设是数据库测试的工作方式。 在本文中,我将解释如何配置您的项目,以使Spring Test和DBUnit在多开发人员环境中协同工作。
我的基本需求是能够测试一些复杂的查询:在进行集成测试之前,我必须验证那些查询是否可以为我提供正确的结果。 这些本质上不是单元测试,但是让我们将它们关联起来。 为了实现这一点,我使用了一个名为DBUnit的框架。 尽管自2010年下半年以来未进行维护,但我还没有找到替代者(请成为我的提案客人)。
我也有一些限制:
考虑到最后一点,我们的组织让我们为每个“单元测试”从每个开发人员一个Oracle模式中受益。
Spring提供了开箱即用的AbstractTestNGSpringContextTests
类。 反过来,这意味着我们可以将TestNG注释以及@Autowired
应用于子类。 这也意味着我们可以访问底层的applicationContext
,但是我不愿意(而且在任何情况下都不需要)。
这种测试的结构如下所示:
@ContextConfiguration(location="classpath:persistence-beans.xml")
publicclassMyDaoTestextendsAbstractTestNGSpringContextTests{
@Autowired
privateMyDaomyDao;
@Test
publicvoidwhenXYZThenTUV(){
...
}
}
熟悉Spring和TestNG的读者在这里不要感到惊讶。
DbUnit是针对数据库驱动项目的JUnit扩展,除其他事项外,它使数据库在测试运行之间进入已知状态。 […] DbUnit能够与XML数据集之间导出和导入数据库数据。 从2.0版开始,在流式模式下使用DbUnit时,它也可以处理非常大的数据集。 DbUnit还可以帮助您验证数据库数据是否与预期的一组值匹配。
DBunit是一个JUnit扩展,应该扩展提供的父类org.dbunit.DBTestCase
。 在我的上下文中,我必须重新定义一些设置和拆卸操作才能使用Spring继承层次结构。 幸运的是,DBUnit开发人员考虑了这一点并提供了相关文档 。
在可用的不同策略中,我的爱好分别是在设置和拆卸时倾向于CLEAN_INSERT和NONE操作。 这样,如果测试失败,我可以直接检查数据库状态。 这样更新了我的测试类:
@ContextConfiguration(locations={"classpath:persistence-beans.xml","classpath:test-beans.xml"})
publicclassMyDaoTestextendsAbstractTestNGSpringContextTests{
@Autowired
privateMyDaomyDao;
@Autowired
privateIDatabaseTesterdatabaseTester;
@BeforeMethod
protectedvoidsetUp()throwsException{
// Get the XML and set it on the databaseTester
// Optional: get the DTD and set it on the databaseTester
databaseTester.setSetUpOperation(DatabaseOperation.CLEAN_INSERT);
databaseTester.setTearDownOperation(DatabaseOperation.NONE);
databaseTester.onSetup();
}
@Test
publicvoidwhenXYZThenTUV(){
...
}
}
当然,我们需要有一个特定的Spring配置文件来注入databaseTester
。 例如,这是一个:
<?xml version="1.0" encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<beanclass="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<propertyname="location"value="${user.name}.database.properties"/>
</bean>
<beanname="dataSource"class="org.springframework.jdbc.datasource.SingleConnectionDataSource">
<propertyname="driverClass"value="oracle.jdbc.driver"/>
<propertyname="username"value="${db.username}"/>
<propertyname="password"value="${db.password}"/>
<propertyname="url"value="jdbc:oracle:thin:@<server>:<port>/${db.schema}"/>
</bean>
<beanname="databaseTester"class="org.dbunit.DataSourceDatabaseTester">
<constructor-argref="dataSource"/>
</bean>
</beans>
然而,不只是眼神。 注意,必须向databaseTester
提供数据源。 由于要求每个开发人员都拥有一个数据库,因此基本上有两种选择:使用内存数据库或使用与生产环境相同的数据库,并为每个开发人员提供一个这样的数据库架构。 我倾向于后一种解决方案(如果可能),因为它倾向于减小测试环境和生产环境之间的差异。
因此,为了使每个开发人员都可以使用自己的模式,我使用了Spring在运行时替换Java系统属性的功能:每个开发人员都具有不同的user.name
来表征。 然后,我配置一个PlaceholderConfigurer
来查找{user.name}.database.properties
文件,如下所示:
db.username=myusername1
db.password=mypassword1
db.schema=myschema1
这使我可以实现每个开发人员使用自己的Oracle实例的目标。 如果要使用此策略,请不要忘记为Continuous Integration Server提供特定的database.properties。
最后,整个测试链都被配置到数据库层。 但是,当运行先前的测试时,一切正常(或不正常),但是在检查数据库时,它看起来没有任何变化。 奇怪的是,如果您确实加载了一些XML数据集并在测试过程中对其进行了断言,则它的行为也相应地:这带有事务问题的所有症状。 实际上,当您仔细查看Spring的文档时,一切都会变得清晰。 Spring的愿景是,与DBUnit完全矛盾的是,运行测试应该使数据库保持不变。 默认情况下,只需在测试结束时回滚所有更改即可实现此目的。
为了更改此行为,唯一要做的就是使用@TransactionConfiguration(defaultRollback=false)
注释测试类。 请注意,这不会阻止我们使用@Rollback
批注指定特定方法,这些方法应视情况而定,不会影响数据库状态。
测试类变为:
@ContextConfiguration(locations={"classpath:persistence-beans.xml","classpath:test-beans.xml"})
@TransactionConfiguration(defaultRollback=false)
publicclassMyDaoTestextendsAbstractTestNGSpringContextTests{
@Autowired
privateMyDaomyDao;
@Autowired
privateIDatabaseTesterdatabaseTester;
@BeforeMethod
protectedvoidsetUp()throwsException{
// Get the XML and set it on the databaseTester
// Optional: get the DTD and set it on the databaseTester
databaseTester.setSetUpOperation(DatabaseOperation.CLEAN_INSERT);
databaseTester.setTearDownOperation(DatabaseOperation.NONE);
databaseTester.onSetup();
}
@Test
publicvoidwhenXYZThenTUV(){
...
}
}
尽管关于数据库测试的Spring和DBUnit视图是反对的,但是Spring的配置多功能性使我们使其能够满足我们的需求(以及DI的好处)。 当然,其他改进也是可能的:在父级测试类中推送通用代码,等等。
翻译自: https://blog.frankel.ch/database-unit-testing-with-dbunit-spring-and-testng/