Grails 3集成规范具有奇怪的交易行为

Gre*_*egg 3 grails integration-testing functional-testing grails3

我有以下测试(可能是功能测试而不是集成,但是...):

@Integration(applicationClass = Application)
@Rollback
class ConventionControllerIntegrationSpec extends Specification {

  RestBuilder rest = new RestBuilder()
  String url

  def setup() {
    url = "http://localhost:${serverPort}/api/admin/organizations/${Organization.first().id}/conventions"
  }

  def cleanup() {
  }

  void "test update convention"() {
    given:
    Convention convention = Convention.first()

    when:
    RestResponse response = rest.put("${url}/${convention.id}") {
      contentType "application/json"
      json {
        name = "New Name"
      }
    }

    then:
    response.status == HttpStatus.OK.value()
    Convention.findByName("New Name").id == convention.id
    Convention.findByName("New Name").name == "New Name"

  }
}
Run Code Online (Sandbox Code Playgroud)

数据是通过BootStrap加载的(这可能是个问题),但是问题是当我在then块中时;它会Convention通过新名称和id匹配项找到,但是在测试该name字段时失败了,因为它仍然具有旧名称。

在阅读有关测试的文档时,我认为问题出在创建数据的会话中。由于的@Rollback会话与分开BootStrap,因此数据并不是真正的胶凝。例如,如果我通过测试given块加载数据,则当调用我的控制器时,该数据不存在RestBuilder

我完全不应该以这种方式进行这种测试,因此建议值得赞赏。

Bur*_*ith 5

这绝对是一项功能测试-您正在针对服务器发出HTTP请求,而不是进行方法调用,然后对这些调用的效果进行断言。

无论测试是否在与服务器相同的JVM中运行,您都无法通过功能测试获得自动回滚,因为调用是在一个线程中进行的,而调用是在另一个线程中进行的。BootStrap中的代码在所有测试运行并提交之前运行一次(因为您在事务中或通过自动提交进行了更改),然后“客户端”测试代码在其自己的新Hibernate会话中运行,并在测试基础架构启动(并将在测试方法结束时回滚),并且服务器端代码在其自己的Hibernate会话中运行(由于OSIV),具体取决于您的控制器和服务是否是否进行事务处理,可能在其他事务中运行,或者可能只是自动提交。

会话缓存可能不是这里要考虑的一件事,但是在Hibernate持久性测试中应该始终考虑的一件事。Hibernate会话是第一级缓存,动态查找器(如)findByName可能会触发所需的刷新,但应在测试中明确说明。但是它不会清除任何缓存的元素,并且冒着使用诸如此类代码的误报的风险,因为您可能实际上并未加载新实例-Hibernate可能正在返回缓存的实例。打电话时肯定会的get()。我总是flushAndClear()在集成和功能基类中添加一个方法,并且putwhen阻止以确保所有内容都从Hibernate刷新到数据库(未提交,只是刷新),并清除会话以强制进行真正的重新加载。只需选择一个随机域类并使用withSession,例如

protected void flushAndClear() {
   Foo.withSession { session ->
      session.flush()
      session.clear()
   }
}
Run Code Online (Sandbox Code Playgroud)

由于这种put情况发生在一个线程/会话/ tx中,并且查找程序在各自的线程中运行,因此这不会产生任何效果,而应该是通常使用的模式。

  • 功能测试主要是一等公民,但是由插件来创建测试/功能文件夹。首先,在Grails 3中,集成测试和功能测试共享同一文件夹,并且启动了用于集成测试的Web服务器,这更加令人困惑,进一步模糊了差异。 (2认同)