【问题标题】:Grails 3 Integration Spec has Strange Transactional BehaviorGrails 3 集成规范具有奇怪的事务行为
【发布时间】:2016-11-05 21:26:29
【问题描述】:

我有以下测试(这可能更像是一个功能测试而不是集成但是......):

@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"

  }
}

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

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

完全有可能我不应该以这种方式进行这种测试,因此我们非常感谢您的建议。

【问题讨论】:

    标签: grails integration-testing functional-testing grails3


    【解决方案1】:

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

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

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

    protected void flushAndClear() {
       Foo.withSession { session ->
          session.flush()
          session.clear()
       }
    }
    

    由于put 发生在一个线程/会话/tx 中并且查找器在自己的线程中运行,因此这应该不会产生影响,但应该是一般使用的模式。

    【讨论】:

    • 谢谢伯特。这有助于澄清一些事情。让我感到困惑的是,在 Grails 2 中,集成和功能之间有一条非常清晰的界线(因为当时功能测试不是一等公民),而在 Grails 3 中,创建功能测试使用 @Integration@Rollback GebSpec 的模板。
    • 功能测试大多是一等公民,但由插件来创建测试/功能文件夹。乍一看,在 Grails 3 中,集成和功能测试共享同一个文件夹,并且为集成测试启动 Web 服务器,这进一步模糊了差异。
    • 哇,这解释了为什么过去几天我无法进行回滚。我认为这方面的事情正在发生。非常感谢你澄清这一点。是否有功能 (API) 测试数据设置和回滚的最佳实践?我目前正在通过 BootStrap 设置我的数据,就像 Gregg 一样,并在测试通过后通过 API 调用撤消清理块中的更改。
    猜你喜欢
    • 2013-03-23
    • 2018-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-14
    • 2016-02-03
    • 1970-01-01
    相关资源
    最近更新 更多