我目前正在进行一些集成(或者功能性,我不是真正的QA而是后端开发,因此我可能会对术语草率)我们项目的REST测试,我们使用的是Grails 3.0.9, Spock Framework 1.0-Groovy-2.4和PostgreSQL DB进行测试。
此外,我们还有用于测试目的的单独数据库,在每次测试后回滚更改仍然至关重要。我查看了Grails testing doc,并尝试使用@Rollback
注释,如示例中所述 - 它只是不起作用,更改仍然提交给数据库。
由于它是工作项目,我无法提供一些真正的代码片段,希望您理解,虽然我的测试规范看起来就像在Grails doc示例中一样:从Specification
,@Integration
扩展而来Grails'@Rollback
注释用于类级别。
import grails.transaction.Rollback
import grails.test.mixin.integration.Integration
@Integration
@Rollback
class SomeSpec extends Specification {
@Shared
RESTClient client
def setup() {
client = new RESTClient('http://localhost:8080')
}
我已经尝试了所有方法:分别对每个方法使用@Rollback,尝试以相同的方式使用Spring的@Rollback
,例子也是错误的 - 它不能应用于类,只能应用于方法,我尝试使用@Transactional
- 没有成功。
另外我应该提到我正在使用的控制器和服务被注释为Grails'@Transactional
,尽管删除所有注释并没有改变任何内容。
我用谷歌搜索了一整天并且找不到任何有用的东西,因为这些解决方案主要针对Grails 2,所以我相信我的问题在SO周围是独一无二的。其中一些解决方案建议使用IntegrationSpec而不是规范,报告它按预期工作,但它已被删除Grails 3并替换为@Integration
,所以我认为这不是我的选择。
我也发现了很多@Transaction
解决方案,但这些解决方案也没有用,尽管我在尝试应用这些解决方案时遇到错误的可能性很小。
因此,目前唯一的选择是尝试使测试尽可能独立于数据,并在每个测试套件之后删除并重新创建数据库,但这是IMO非常糟糕的解决方案。我不相信没有人像我一样遇到同样的问题,所以我希望找到一些可以接受的解决方案。当然,我很乐意提供我忘记提及的任何缺失信息。
更新:我已经尝试了提到的解决方法here - 我已经使用了Transactional,正如我之前所描述的那样,它仍然没有效果,仍然会提交更改,虽然我为每个测试都得到了这对消息,这意味着它实际上是在尝试做某事。不确定它会有所帮助,但它在这里(它被编辑了一点):
INFO org.springframework.test.context.transaction.TransactionContext - Began transaction (1) for test context [DefaultTestContext@3dbe8e11 testClass = SomeSpec, testInstance = package.SomeSpec@5e2296ae, testMethod = $spock_feature_0_0@SomeSpec, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@6ec335b0 testClass = SomeSpec, locations = '{}', classes = '{class package.Application}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.IntegrationTest=true}', resourceBasePath = '', contextLoader = 'grails.boot.config.GrailsApplicationContextLoader', parent = [null]]]; transaction manager [org.grails.orm.hibernate.GrailsHibernateTransactionManager@25870a3a]; rollback [true]
INFO org.springframework.test.context.transaction.TransactionContext - Rolled back transaction for test context [DefaultTestContext@3dbe8e11 testClass = SomeSpec, testInstance = package.SomeSpec@5e2296ae, testMethod = $spock_feature_0_0@SomeSpec, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@6ec335b0 testClass = SomeSpec, locations = '{}', classes = '{class package.Application}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.IntegrationTest=true}', resourceBasePath = '', contextLoader = 'grails.boot.config.GrailsApplicationContextLoader', parent = [null]]].
答案 0 :(得分:1)
我遇到了同样的问题,但就我而言,我注意到我用@LoadDataSet
加载到数据库的所有内容都被正确回滚。但是,当我与外键引用的列进行任何交互时,@rollback
只是不删除引用,这会导致错误。我也尝试了很多@Transactional
解决方案,但仍然无法正常工作。
我的解决方法是在DomainClassName.executeUpdate('delete from DomainClassName')
之前调用cleanup()
到我期望更改的所有domainClasses。现在,这些测试工作得很糟糕,不太合适。
检查this是否适合您。
只是为了澄清:我在我的测试类中使用@Transactional(propagation = Propagation.NESTED)
的组合解决了我的问题,然后,作为每个测试的最后一步,将DomainClassName.executeUpdate('delete from DomainClassName')
调用到每个域类I' m期待改变。
我的测试类现在是这样的:
@Integration
@Transactional(propagation = Propagation.NESTED)
@TestFor(MyService)
@LoadDataSet("PathInResources") //If needed
class MyTestSpec extends GebSpec {
static transactional = true
@Autowired
SessionFactory session
private void tearDownValues(){
DomainClassName.executeUpdate('delete from DomainClassName') // Hibernate mapping will handle the table name for you
}
public void myTest() {
//Test steps ...
then:
tearDownValues() //Call your tearDown method on the last step, after that and before cleanup(), auto rollback runs and you may have the same error as before
}
请注意,如果您使用@LoadDataSet
,则只需要为您在测试中创建的数据调用DomainClassName.executeUpdate('delete from DomainClassName')
。 Spock将处理@LoadDataSet
创建的所有内容的回滚。
答案 1 :(得分:0)
使用
注释测试类@WebIntegrationTest
@Transactional
使用
注释方法@Rollback
在由
包装的DataSource上运行org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
.newInstance(dataSource)