事务

当你运行`rails generate rspec:install`时,`spec/rails_helper.rb` 文件包含以下配置

RSpec.configure do |config|
  config.use_transactional_fixtures = true
end

这个设置的名称有点误导人。它在 Rails 中的真正含义是“在事务中运行每个测试方法”。在 rspec-rails 的上下文中,它意味着“在事务中运行每个示例”。

想法是,每个示例都从一个干净的数据库开始,创建该示例所需的任何数据,然后在示例结束时通过简单地回滚事务来删除这些数据。

禁用事务

如果你更喜欢自己管理数据,或者使用其他工具(如database_cleaner)来为你管理数据,只需告诉 RSpec 告诉 Rails 不要管理事务

RSpec.configure do |config|
  config.use_transactional_fixtures = false
end

在`before(:example)`中创建的数据将被回滚

你在`before(:example)`钩子中创建的任何数据都将在示例结束时被回滚。这是一件好事,因为它意味着每个示例都与其他示例留下的状态隔离开来。例如

describe Widget do
  before(:example) do
    @widget = Widget.create
  end

  it "does something" do
    expect(@widget).to do_something
  end

  it "does something else" do
    expect(@widget).to do_something_else
  end
end

上面的两个示例中都重新创建了`@widget`,因此每个示例都有一个不同的对象,并且底层数据被回滚,因此每个示例中`@widget`支持的数据都是新的。

在`before(:context)`中创建的数据不会被回滚

`before(:context)`钩子在事务打开之前被调用。你可以用它来加速,在组中的任何示例运行之前创建一次数据,但是,这会带来一些复杂性,你应该只在完全理解其含义的情况下这样做。以下是一些准则

  1. 确保在`after(:context)`钩子中清理所有数据

    before(:context) do
      @widget = Widget.create!
    end
    
    after(:context) do
      @widget.destroy
    end
    

    如果你不这样做,你就会留下一些数据,这些数据最终会干扰其他示例。

  2. 在`before(:example)`钩子中重新加载对象。

    before(:context) do
      @widget = Widget.create!
    end
    
    before(:example) do
      @widget.reload
    end
    

即使每个示例中的数据库更新将被回滚,但对象不会知道这些回滚,因此对象及其支持的数据很容易不同步。