読者です 読者をやめる 読者になる 読者になる

database_cleaner と use_transactional_fixtures = true の競合について

rails Ruby

競合するというか合わせて使うと不毛なことにしかならない。

capybara で selenium driver など(rack_test driver以外)を使うと別スレッドでサーバが立ち上がり DB コネクションが共有されなくなるため、トランザクションを張ってテストデータを作ると、別スレッドからテストデータにアクセスできなくなる。

下記リンク先の記事(図)を参考。

The One True Guide to database transactions with Capybara - Ian's Tech Notes

例えば、roles テーブルを参照する users テーブルがあり、下記のように roles にテストデータをつくるとき use_transactional_fixtures = true としていると、テストデータが commit されないので別スレッドで立ち上がってるサーバ側から roles のレコードにアクセスできず users のレコードの作成に失敗する。

...
  # use_transactional_fixtures = true

  before do
    FactoryGirl.create(:role) # uncommited transaction
  end

  it "新規作成できること", js: true do
    visit new_user_path
    fill_in "name", with: "Taro"
    click_button "Create User" # 必要な role が見当たらずエラー
    
    expect(page).to have_content 'Taro'
  end
...

解決として use_transactional_fixtures = false にすると良いということだった。

ただし、すると今度は spec/fixtures/*.yml が毎度作りなおされるようになる(delete & insert)。

これが気に食わないと思って避けるためにパッと思いついた案が use_transactional_fixtures = true として、DatabaseCleaner.start, clean しないことだったけど、それでどうにかなるのは DatabaseCleaner.strategy = :transaction で selenium driver 等を使わない場合に限られてきそう。

そうすると clean_with(:truncation) のために database_cleaner を入れたみたいになるので、素直に use_transactional_fixtures = false とした方がよかった。

おわり

database_cleaner を使うときは、fixtures の作りなおしを受け入れて、 use_transactional_fixtures = false としたい。(結果、ドキュメント通り)

あと aamine/activerecord4-redshift-adapter の場合、:truncation, :deletion 共に動かないので、database_cleaner を使うメリットがなさそうだった。