Capybara-webkitによる高速インテグレーションテストまとめ
このテのエントリも何度目になるかわかりませんが、
実際に使ってきて「これだ!」という環境がある程度確立できた気がするので
一旦ここにまとめておきます。Ruby1.9.3、Rails3.2.2で動作確認。
- 参考:
使うものリスト:
ライブラリ名 | ちょっとした解説 |
---|---|
Spork | テストの高速化をしてくれるDRbサーバ |
RSpec2 | 言わずと知れた「動く仕様書」 |
Guard | ファイルを監視し、変更があればテストを自動実行*1 |
Capybara | 独自のDSLでインテグレーションテストを可能にする |
CapybaraWebkit | CapybaraでJavaScriptの動作確認をするためのエンジン |
FactoryGirl | 「fixtureの代わり」とだけ表現するにはあまりにも惜しい多機能なモデルテンプレートエンジン |
Headless | ブラウザを画面上に表示せずにテストするためのラッパーライブラリ |
DatabaseCleaner | Capybaraへデータを渡すとき、渡したあとのデータ管理 |
Rails3Generators | FactoryGirl用の定義を自動生成してくれるジェネレータ |
autotestからGuardへ乗り換えたのは、Guardが常にプロンプトを表示しており、
rと叩くだけでSporkの再起動から各種specファイルの実行まで行なってくれる他、
Macならgrowl、Linux系ならlibnotifyなど、通知までサポートしてくれる小粋な奴だからです。*2 *3
早速Gemfile:
group :development, :test do gem 'spork' gem 'rspec-rails' gem 'guard-spork' gem 'guard-rspec' gem 'capybara' gem 'capybara-webkit' gem 'factory_girl_rails' gem 'headless' gem 'database_cleaner' gem 'rails3-generators' end
シェル:
$ sudo apt-get install xvfb # headlessのための仮想ディスプレイライブラリ $ sudo apt-get install libqt4-dev # capybara-webkitのためのqt4ライブラリ $ bundle install $ bundle update $ rails g rspec:install $ spork --bootstrap
ここでSporkの設定が spec/spec_helper.rb の先頭に追加されているので、
Spork.prefork ブロックに元々の spec_helper.rb の内容を全て流し込みます。
そして、各gemの設定もついでにしてしまいましょう。
- 2012-04-07追記: Sporkはテスト実行時にモデルをリロードしないようなので、
- http://blog.twiwt.org/e/cafcfe
- こちらを参考に Spork.each_run ブロックに追記しました。
spec/spec_helper.rb(一部コメントなどを消去しています):
Spork.prefork do # headless のための設定。 # これをしないとテストのたびにブラウザが表示される(かもしれない) require "headless" headless = Headless.new(display:99) headless.start at_exit{ headless.destroy } # ここから下は元々の spec_helper.rb の内容 ENV["RAILS_ENV"] ||= 'test' require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails' require 'rspec/autorun' Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} RSpec.configure do |config| config.use_transactional_fixtures = true config.infer_base_class_for_anonymous_controllers = false # :focus => true のテストのみ実行する config.treat_symbols_as_metadata_keys_with_true_values = true config.filter_run :focus => true config.run_all_when_everything_filtered = true # database_cleanerの設定 config.before :suite do DatabaseCleaner.strategy = :truncation DatabaseCleaner.clean_with :truncation end config.before :each do if example.metadata[:js] 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 end Capybara.javascript_driver = :webkit # ドライバをcapybara-webkitに(デフォルトは :selenium) end Spork.each_run do FactoryGirl.reload # ファクトリの定義が実行ごとにリロードされる silence_warnings do # 2012-04-07追記 Dir[Rails.root.join('app/**/*.rb')].each{ |file| load file } # end # end
続いてGuardの設定:
$ bundle exec guard init spork $ bundle exec guard init rspec
このコマンドで Guardfile というものが作成されているはずです。
一見すると長くて記号だらけで読む気も起きませんが、
単に監視するファイルを正規表現で指定し、
対応するspecファイルを実行するようにしているだけです。
今回はFactoryGirlを使う他、
SporkとはDRbを使って通信してもらわなければならないので、
そこんところをちょっとだけ修正します。
# ↓コメントアウト # guard 'rspec', :version => 2 do guard 'rspec', :version => 2, :cli => '--drb' do # rspecの引数に --drb を渡すことでSporkと通信してくれます watch(%r{^spec/factories/(.+)\.rb$}) { "spec/models" } # 追加 # ... end
モデルを作成するとき、rails3-generatorsが spec/factories/ に
ファクトリファイルのひな形を置いてくれるようになっているので、
そのフォルダも監視してあげましょうという感じ。
そしてラスト、config/application.rb:
# Generators module YourAppName class Application < Rails::Application # ...略... config.generators do |g| g.test_framework :rspec, fixture:true, views:false g.fixture_replacement :factory_girl end end end
これでようやくスタートラインに立てました。
さっそくGuardを実行します。
$ bundle exec guard Guard uses NotifySend to send notifications. Guard is now watching at '/home/sandmark/sites/YourAppName' Starting Spork for RSpec Using RSpec Preloading Rails environment Loading Spork.prefork block... Spork is ready and listening on 8989! Spork server for RSpec successfully started Guard::RSpec is running, with RSpec 2! Running all specs Running tests with args ["--drb", "-f", "progress", "-r", "/home/sandmark/.rvm/gems/ruby-1.9.3-p125/gems/guard-rspec-0.7.0/lib/guard/rspec/formatters/notification_rspec.rb", "-f", "Guard::RSpec::Formatter::NotificationRSpec", "--out", "/dev/null", "--failure-exit-code", "2", "spec"]... Run options: include {:focus=>true}
すでにモデルやコントローラを作成してあった場合は
大量の pending が出迎えてくれることでしょう。
ともあれ、何も問題がなければここでGuardのプロンプトが表示されているはずです。
(詳細については help と入力してEnterしといてください。)
さて、実際のspecファイルはこんな感じになります。
spec/requests/users_spec.rb:
describe "Users" do describe "GET /users" do before do @user = FactoryGirl.create(:user) visit "/users" end context "when 'about username' link was clicked," do it "should show user's name", :focus => true do click_link "about #{@user.name}" page.should have_content(@user.name) end it "should pop-up hidden message for JavaScript", :js => true do click_link "about #{@user.name}" page.should have_content("Woo Hoo!") end end # context end # GET /users end # Users
ここで it ディレクティブに渡しているのは :js と :focus ですが、
今回のエントリに直接関係があるのは :js オプションのほうです。
spec_helper.rb にも書きましたが、
:js => true を設定していると capybara-webkit がJavaScriptエンジンとして動き、
Capybaraがブラウザ(今回のケースではwebkit-server)を起動、接続して動作をチェックするわけです。
しかし headless を導入しているのでブラウザのウィンドウは画面に表示されず、
メモリ上の仮想ディスプレイでうまいことテストしてくれる、というからくり。*4
ただ :webkit が呼び出されたときは webkit-server にデータを渡さなければならないので
RSpec2のタイミングでデータをリセットされると困ります。
そのため、 spec_helper.rb の DatabaseCleaner の設定部分で
一時的に RSpec2 でのデータ管理を止め、 DatabaseCleaner に任せています。
そして :focus => true のときは、Guardが変更を感知し、テストを開始しても、
:focus => true が設定されているテストのみを実行してくれるものです。
ひとつの機能を集中的に実装したい場合は特に役立つでしょう。
というわけで以上、もっと小さくまとめるつもりでしたが冗長な説明になってしまいました。
VMwareからネイティブUbuntuに変えたときに異常にテストが遅かった(50秒近くかかった)ので、
あるあ…ねーよwww と思って調査した次第です。
少しでもお役に立てれば。
それでは Happy Testing!