我创建了一个公共 Github 存储库来复制和说明该问题:
https://github.com/SparkersData/grails414-dataservice-int-test
集成测试的目的是通过调用事务服务来更新现有的 Book 对象,事务服务又调用 GORM 数据服务来更新 Book。
BookServiceIntSpec -> [ BookService -> BookDataService ]
方括号之间部分的执行应由
@Transactional
的
BookService
注解引入的 Transaction 来保护
我创建了 3 种不同的 Spec 方法来演示奇怪的行为。
集成测试的序言如下:
import acme.domain.Book
import grails.gorm.transactions.Rollback
import grails.testing.mixin.integration.Integration
import spock.lang.Specification
import spock.lang.Subject
@Integration
@Rollback
@Subject(BookService)
class BookServiceIntSpec extends Specification {
BookService bookService
Book book
private void setup() {
book = new Book(title: "The Hobbit")
book.save(flush: true, failOnError: true)
}
}
BookService 中的生产代码:
Book updateWithGORM(Book toUpdate) {
toUpdate.save(flush: true, failOnError: true)
}
这是集成测试:
def "It is able to update a Book without creating a new transaction, by using GORM in the service."() {
when:
book.title = updatedTitle
bookService.updateWithGORM(book)
then:
Book.findByTitle(updatedTitle) != null
where:
updatedTitle = "Bilbo The Hobbit"
}
BookService 中的生产代码
Book updateWithDataService(Book toUpdate) {
bookDataService.save(toUpdate)
}
这是集成测试:
def "It is NOT able to update a Book without creating a new transaction, if using a Data Service."() {
when:
book.title = updatedTitle
bookService.updateWithDataService(book)
then:
Book.findByTitle(updatedTitle) == null //<1>
where:
updatedTitle = "Bilbo The Hobbit"
}
<1> 请求更新后找不到更新的书籍
Book updateWithDataService(Book toUpdate) {
bookDataService.save(toUpdate)
}
这是集成测试:
def "It is able to update a Book by creating a new transaction, if using a Data Service."() {
when:
book.title = updatedTitle
Book.withNewTransaction { //<1>
bookService.updateWithDataService(book)
}
then:
Book.findByTitle(updatedTitle) != null //<2>
where:
updatedTitle = "Bilbo The Hobbit"
}
<1> 通过withNewTransaction,更新成功
<2> 请求更新后可以找到更新的书
这要么是 Grails 4.1.4 集成测试运行时中的一个错误,要么是文档不清楚如何在集成测试中使用事务服务。我对行为解释的猜测:
还有其他人偶然发现这种行为或对此有解释吗?用
withNewTransaction
结束每个通话似乎是一场噩梦。
通过修改服务方法如下:
Book updateWithDataService(Book toUpdate) {
def res = bookDataService.save(toUpdate)
sessionFactory.currentSession.flush()
res
}
测试行为正常,不必混乱创建新事务,也不会干扰 Hibernate 会话。
我仍然不明白为什么这不是默认行为!
值得注意的是,在 grails 集成测试中的设置中添加的数据不会在测试运行之间被清除,除非您自己执行此操作,因此在第三次测试结束时,您的 book 表中将出现 3 行,设置使用不同的交易到测试交易。我认为这不会影响您这次的测试,但我怀疑这是否是您想要的。 您可以在测试中调用的单独方法中进行设置,也可以添加清理,例如......
void cleanup() {
Book.where {}.deleteAll()
}
不是 100% 确定原因,但我认为您对 findByTitle 的调用受到缓存、切换到动态查找器 get 的影响,或者 findById 或 get 在您的数据服务上获取更新的书籍(在示例中添加了两者),例如
def "It is able to update a Book by creating a new transaction, if using a Data Service."() {
when:
book.title = updatedTitle
bookService.updateWithDataService(book)
then:
bookDataService.get(book.id).title == updatedTitle //<2>
Book.findById(book.id).title == updatedTitle
where:
updatedTitle = "Bilbo The Hobbit"
}
您在非数据服务示例中刷新,这就是我认为没问题的原因。