WEBサービス創造記

WEBサービスを作ったり保守したりしてる人のメモブログです。

Sporkでテストを高速化する

      2015/05/24

Sporkについて

テストの実行を高速化するテストサーバのgem。
通常Railsはテスト実行時に一度ロードされるが、sporkは予めRailsをロードした状態でテストを行うので高速である。

Sporkのインストール

Gemfileに`gem spork`を追記して`bundle install`を実行する。

group :test, :development do
  〜(略)〜
  gem 'spork'
end

Sporkの初期設定

以下のコマンドを実行する

$ spork --bootstrap

これでspec_helper.rbの先頭にSpork.preforkとSpork.each_runというメソッド呼び出しが追加される。
Spork.preforkに渡すブロックはサーバー起動時に実行され、Spork.each_runに渡すブロックはテストのたびに実行される。
この段階でspec_helper.rbに定義されていた処理はすべて、Spork.preforkのブロックの中に移動しておくといい。

Sporkの使い方

RSpecでのテスト

まず、新しいコンソールを開いてsporkサーバを起動する。

$ spork

Sporkサーバが起動している状態でrspecに–drbオプションを付けて実行すると、spork側でテストが行われる。

$ rspec --drb -f d spec/models/user_spec.rb

Cucumberでのテスト

cucumberをまだ初期化してないなら以下のコマンドを実行すればOK。

$ rails g cucumber:install --spork

以前にcucumber:installを実行していて、既に設定ファイルなどの生成などが完了している場合は、上記コマンドを実行することで編集内容の競合が発生するおそれがある。
そのようなケースを避けたい場合は、”config/cucumber.yml”と”features/support/env.rb”をそれぞれ以下のように編集すればOK。

“config/cucumber.yml”を以下のように編集する(–drbオプションを付加する)。

- default: <%= std_opts %> features
- wip: --tags @wip:3 --wip features
- rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip

+ default: --drb <%= std_opts %> features
+ wip: --drb --tags @wip:3 --wip features
+ rerun: --drb <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip

続いて”features/support/env.rb”に以下を追記。

require 'spork'
 
Spork.prefork do
  require 'cucumber/rails'


  # Capybara defaults to XPath selectors rather than Webrat's default of CSS3. In
  # order to ease the transition to Capybara we set the default here. If you'd
  # prefer to use XPath just remove this line and adjust any selectors in your
  # steps to use the XPath syntax.
  Capybara.default_selector = :css

end
 
Spork.each_run do
  # By default, any exception happening in your Rails application will bubble up
  # to Cucumber so that your scenario will fail. This is a different from how 
  # your application behaves in the production environment, where an error page will 
  # be rendered instead.
  #
  # Sometimes we want to override this default behaviour and allow Rails to rescue
  # exceptions and display an error page (just like when the app is running in production).
  # Typical scenarios where you want to do this is when you test your error pages.
  # There are two ways to allow Rails to rescue exceptions:
  #
  # 1) Tag your scenario (or feature) with @allow-rescue
  #
  # 2) Set the value below to true. Beware that doing this globally is not
  # recommended as it will mask a lot of errors for you!
  #
  ActionController::Base.allow_rescue = false
  
  # Remove/comment out the lines below if your app doesn't have a database.
  # For some databases (like MongoDB and CouchDB) you may need to use :truncation instead.
  begin
    DatabaseCleaner.strategy = :transaction
  rescue NameError
    raise "You need to add database_cleaner to your Gemfile (in the :test group) if you wish to use it."
  end
  
  # You may also want to configure DatabaseCleaner to use different strategies for certain features and scenarios.
  # See the DatabaseCleaner documentation for details. Example:
  #
  #   Before('@no-txn,@selenium,@culerity,@celerity,@javascript') do
  #     DatabaseCleaner.strategy = :truncation, {:except => %w[widgets]}
  #   end
  #
  #   Before('~@no-txn', '~@selenium', '~@culerity', '~@celerity', '~@javascript') do
  #     DatabaseCleaner.strategy = :transaction
  #   end
  #
end

これは`spork –bootstrap`を実行したときにspec_helper.rbに追加されたものと同じ。
RSpecのときと同様に、既にenv.rbに定義されている処理はすべてSpork.preforkのブロックの中に移動しておくといい。

config/cucumber.ymlの編集により、Spork側でテストを実行することがデフォルトとなっているので、いつもと同じように`rake cucumber`を実行すればOK。

ただし、下記のようにSporkでCucumberを使うことを明示しないと”WARNING: No DRb server is running. Running features locally:”のようなエラーが発生するので注意。

$ spork cucumber

クラスキャッシュの無効化

デフォルトではアプリケーションのコードを更新してもSporkを再起動しないと反映されない。
この挙動を変えるには、”config/environments/test.rb”に以下を追記する。

AppName::Application.configure do
  〜(略)〜
  # Spork 使用時にクラスをキャッシュしないようにする
  if defined?(Spork) && Spork.using_spork?
    config.cache_classes = false
  else
    config.cache_classes = true
  end
end

参考資料

 - Ruby on Rails ,