RSpec2 + Capybara-Webkitでの注意点

昨日の記事 id:sandmark:20120324 に不備があったので補足です。
参考サイト: kinopyo blog - Learning through Writing

RSpec2はテストをそれぞれ実行するときデータベースをクリアしてくれますが、
capybara-webkit を使ったとき(it "hoge", :js => trueのとき)は
データが渡されない+クリアされない=テストが動かないので、何とかしてやる必要があります。

gem 'database_cleaner' を使ってRSpec2に設定をしてやります。
Gemfile:

group :test, :development do
  gem "database_cleaner"
end

spec/spec_helper.rb:(2012-04-03: 間違いです。後述。)

RSpec.configure do |config|
  # ...
  #
  # If you're not using ActiveRecord, or you'd prefer not to run each of your
  # examples within a transaction, remove the following line or assign false
  # instead of true.
  config.use_transactional_fixtures = false   # true から false へ修正

  # 以下 DatabaseCleaner 用の設定を追記  
  config.before(:suite) do
    DatabaseCleaner.strategy = :truncation
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
  end
end

これによってテストのたびに DatabaseCleaner が実行され、
テスト用データベースをクリーンに保つことができます。

2012-04-03: 追記

テスト用データベースがクリーンに保たれるのは実に素晴らしいことなんですが、
ひとつひとつのテストのたびにデータを保存・クリアしているとやたら遅くなります。
90個弱あるテストが50秒弱で終わったので、ひとつのテストにつき2秒近くかかっている計算になります。

それはあんまりにもあんまりなので、DatabaseCleanerには必要なときだけ動いてもらいます。
というわけで上記 spec/spec_helper.rb の設定は間違い*1で、
現時点(database_cleaner0.7.2)では以下のコードが最適解のようです。

RSpec.configure do |config|
  # ...
  #
  # If you're not using ActiveRecord, or you'd prefer not to run each of your
  # examples within a transaction, remove the following line or assign false
  # instead of true.
  config.use_transactional_fixtures = true       # true のままにしておく

  # If true, the base class of anonymous controllers will be inferred
  # automatically. This will be the default behavior in future versions of
  # rspec-rails.
  config.infer_base_class_for_anonymous_controllers = false

  # database_cleaner
  config.before :suite do
    DatabaseCleaner.strategy = :truncation
    DatabaseCleaner.clean_with :truncation       # 追記
  end

  config.before :each do
    if example.metadata[:js]                     # example.metadata で it ディレクティブへの引数を参照できる
      # JavaScriptを使うテストではデータの受け渡しが不可欠なので、
      # 一時的にフィクスチャがクリアされないようにする。
      self.use_transactional_fixtures = false    # ←重要
      DatabaseCleaner.start
    end
  end

  config.after :each do
    if example.metadata[:js]
      DatabaseCleaner.clean                      # データをクリアし、
      self.use_transactional_fixtures = true     # フィクスチャの受け渡しも元へ戻す
    end
  end

  Capybara.javascript_driver = :webkit
end

それでは改めて、みなさん快適なテスト駆動開発を!

*1:厳密には間違いではなく動作します。致命的に遅いだけ。