7

在我们使用 MySql 的 Rails 4.0 应用程序中,我们使用 rspec 和配置了 strategy :transaction 的 database_cleaner gem 来为每个测试用例清理我们的数据库。如果我们有应该回滚的自定义事务,它就不起作用。

没有 database_cleaner gem,只使用标准方式:

config.use_transactional_fixtures = true

一切都按方面进行。但是为了使用 JavaScript 运行特性测试,我们需要 database_cleaner 将夹具删除策略更改为 :truncation。

我们如何将 database_cleaner 与自定义事务一起使用,为什么它与标准 rspec 事务策略不同?

4

1 回答 1

11

问题是 database_cleaner 在测试结束时调用 ActiveRecord.rollback - 您也在代码中使用它。InnoDB/MySQL 不支持真正的嵌套事务,因此代码中的嵌套事务不会被视为真正的事务,除非它们被显式调用。

考虑这个块(来自 ActiveRecord 文档):

User.transaction do
  User.create(username: 'Kotori')
  User.transaction do
    User.create(username: 'Nemu')
    raise ActiveRecord::Rollback
  end
end

您期望在回滚调用之后会发生什么?您希望它回滚 Nemu 用户,并留下 Kotori,对吗?好吧,实际上发生的是 Kotori 和 Nemu 都被创建了。回滚不会触发,因为您处于嵌套事务中(并且 AR 目前只关心父事务)并且父事务(具有实际的数据库事务)没有看到回滚调用——因为它被调用一个孤立的块。有点奇怪。

解决方案

Klass.transaction(requires_new: true)

如果设置 requires_new,ActiveRecord 将使用或伪使用嵌套事务(对于 Postgres,它将进行嵌套事务,对于 MySQL/InnoDB,它将创建保存点)。当您使用 ActiveRecord 调用回滚时,它会确定其范围并发出正确的回滚。

另一种解决方案是使用截断或删除作为涉及事务的测试的策略。

于 2013-10-23T18:13:55.650 回答