注意
この訳はだいぶ古い(2011年7月時のREADME)です。最新版の訳をgithub上に載せたのでこちらをご覧ください。
はじめに
Rails のエンドツーエンドテスト用のデファクトスタンダードプラグイン Capybara の README 意訳です。いつもと比べて直訳成分多めです。
テスト関連はどうにも日本語の情報が少なくて、覚えるのが大変ですね><
概要
Capybara は Rack アプリ(Rails, Sinatra, Merb等)の統合テストを簡単にするのが目的です。Capybara は現実のユーザがウェブアプリとやりとりするのをシミュレートします。テスト用のドライバを選択できます。デフォルトでは Rack::Test と Selenium ドライバをビルトインでサポートしています。HtmlUnit, env.js は外部の gem としてサポートしています。
完全なリファレンスは こちら。
インストール
sudo gem install capybara
OSX は libffi をインストールする必要があるかもしれません。MacPorts では下記のコマンドでインストールできます。
sudo port install libffi
開発
省略
Cucumber で Capybara を使う
省略
RSpec で Capybara を使う
もし Cucumber よりも RSpec を使いたい場合は、下記の一行を spec_helper.rb などに追加することで、RSpec のサポートを使えるように出来ます。
require 'capybara/rspec'
そうすると例えば下記のように spec が書けます
describe "the signup process", :type => :request do before :each do User.make(:email => 'user@example.com', :password => 'caplin') end it "signs me in" do within("#session") do fill_in 'Login', :with => 'user@example.com' fill_in 'Password', :with => 'password' end click_link 'Sign in' end end
Capybara は :type => :request とタグ付けされた example group の中だけ使えます(もしくは Steak 用に :acceptaance でも使えます)
もし Rails でテストをしていて、rspec-rails gem を使っているなら、これらの :request の example group には見覚えがあるかもしれません。なぜなら、これらは RSpec バージョンの統合テストだからです。つまり、Capybara のヘルパに加えて、named route も同時に使えます。例えば visit edit_user_path(user) のように。spec/requests にファイルを置けば、 rspec-rails は自動に :type => :request のタグを付けてくれます。(実際には、spec/integration と spec/acceptance でもうまく動きます)
rspec-rails は :controller と :mailer の example group 中で Capybara を自動で include します。
RSpec のメタデータ機能はドライバを切り替えるのに使えます。:js => true とすると javascript ドライバ、または :driver オプションで特定のドライバを使うことが出来ます。
describe 'some stuff which requires js', :js => true do it 'will use the default js driver' it 'will switch to one specific driver', :driver => :celerity end
Capybara はビルトインのDSLを使うことで受け入れテストを書くことができます
feature "Signing up" do background do User.make(:email => 'user@example.com', :password => 'caplin') end scenario "Signing in with correct credentials" do within("#session") do fill_in 'Login', :with => 'user@example.com' fill_in 'Password', :with => 'caplin' end click_link 'Sign in' end end
これは実際には、単なる request spec へのショートカットで、feature は describe ... :type => :request のエイリアスです。background は before 、 scenario は it/specify のエイリアスです。
Test::Unit で Capybara を使う
Test::Unit で Capybara を使うには、DSL を 使いたいテストのクラスで DSL を include する必要があります(Capybara のバージョンが 0.4.x までの場合は include Capybara、 それ以降は include Capybara::DSL とします)。もしテストのクラスが ActionDispatch::IntegrationTest を継承している場合はこのように書きます。
class ActionDispatch::IntegrationTest include Capybara::DSL end
Test::Unit はメタデータによるドライバの切り替えをサポートしていません。setup や teardown メソッドを使うことでドライバを切り替えます。詳しくは "ドライバの選択" の章をご覧ください。
Ruby on Rails で Capybara を使う
Rails を使うなら、下記の行を加えると、自動で Rails アプリのテスト用の Capybara の設定をしてくれます。(訳注: 恐らく Rails で Test::Unit を使う場合の話だと思われます)
require 'capybara/rails'
Rack で Capybara を使う
Rails 以外の Rack アプリで Capybara を使うなら、Capybara.app に application class を設定します。
Capybara.app = MyRackApp
ドライバの選択
デフォルトでは、Capybara は :rack_test ドライバを使います。rack_test は速いけど JavaScript をサポートしていません。デフォルトのドライバを selenium に変えたい場合は下記のようにします
Capybara.default_driver = :selenium
しかし、もし RSpec か Cucumber を使っているなら、rack_test をデフォルトドライバとして使わないようにして、 :js => true または @javascript と印を付けたところだけ js が利用可能なドライバを使うことが出来ます。js をテストするデフォルトは :selenimum ドライバです。Capybara.javascript_deiver を変更することで、js 用のデフォルトドライバを変更できます。
また、一時的にドライバを変更したい場合は下記のようにします。(Before/setup や After/teardown ブロック中などでよく使われます)
Capybara.current_driver = :culerity # 一時的に変更 ... tests ... Capybara.use_default_driver # デフォルトドライバに戻す
ドライバの変更は新しいセッションを作ります。テストの途中ではドライバの変更は出来ません。
RackTest
RackTest は Capybara のデフォルトドライバです。これは pure Ruby で書かれていて、JavaScript はサポートしていません。Rack Test ドライバは直接 Rack のインタフェースに作用するので、テスト用のサーバーを立ち上げる必要はありません。つまり Rack アプリでなければ、このドライバを使うことは出来ないということです。Rack Test はリモートのアプリのテストにも使えません。capybara-mechanize はリモートのサーバをテストするドライバです。
RackTest はこのように設定することが出来ます。
Capybara.register_driver :rack_test do |app| Capybara::RackTest::Driver.new(app, :browser => :chrome) end
詳しくは "ドライバを設定、追加する" のセクションを見てください。
Selenium
現在、Capybara は Selenium 2.0 をサポートしており、Selenium RC はサポートしていません。Firefox がインストールされていた場合、設定は全てすんでおり、すぐに Selenium を使い始めることが出来ます。
Capybara はデフォルトで Ajax リクエストがページに対して作用し終わるのを待ちます。:resynchronize を false にすることでこの振る舞いをオフに出来ます。詳しくは configuring drivers のセクションを読んでください。
Selenium は transactional fixture をサポートしていません。Transaction Fixtures の章を読んでください。
HtmlUnit
外部の gem としてメンテナンスされている、HtmlUnit を使うドライバが 3つあります。
- Akephalos
- 今のところ HtmlUnit を使うドライバとしては一番かもしれません
- Celerity
- JRuby 上でのみ動きます。なので JRuby で celerity gem をインストールする必要があります。(jruby -S gem install celerity)
- Culerity
- celerityを上記のようにインストールして、JRubyがパスにあるか確認してください。Culerity は現在 Ruby 1.9 では動かないようです。
HtmlUnit は transactional fixtures をサポートしていません。Transactional Fixtures の章を見てください。
env.js
capybara-envjs ドライバは env-js gem を使ってブラウザを使わずに JavaScript を実行します。下記のようにインストールします
gem install capybara-envjs
さらなる情報を得たい場合は、上記のリンクを見てください。envjs gem は現在 Ruby 1.8.7 のみサポートしています。
Envjs は transactional fixtures をサポートしていません。Transactional Fixtures の章を見てください。
DSL
Capybara の DSL は Webrat からインスパイアしています。多くのケースで後方互換性を保っている一方、重要な違いもいくつかあります。Webrat とは異なり、Capybara は 全ての検索で case sensitive です。これは、 Capybara は case insensivity をサポートしていない XPath をヘビーに使っているためです。
Navigating
visit('/projects') visit(post_comments_path(post))
visit メソッドは引数を一つだけ取り、リクエストはいつも GET です。
テスト用に、カレントパスを取得することが出来ます。
current_path.should == post_comments_path(post)
Clicking links and buttons
フルのリファレンスはこちら: Capybara::Node::Actions
リンクやボタンを押すことが出来ます。Capybara は自動でリダイレクトに対応し、ボタンに関連するフォームをサブミットします。
click_link('id-of-link') click_link('Link Text') click_button('Save') click_on('Link Text') # リンクとボタンどちらかをクリック click_on('Button Value')
Interacting with forms
フルのリファレンスはこちら: Capybara::Node::Actions
フォーム要素を操作するツールがたくさんあります
fill_in('First Name', :with => 'John') fill_in('Password', :with => 'Seekrit') fill_in('Description', :with => 'Really Long Text...') choose('A Radio Button') check('A Checkbox') uncheck('A Checkbox') attach_file('Image', '/path/to/image.jpg') select('Option', :from => 'Select Box')
Querying
フルのリファレンスはこちら: Capybara::Node::Matchers
Capybara はページに特定の要素が存在しているかを調べることが出来るオプションを豊富に持っており、かつそれらの要素の操作もできます。
page.has_selector?('table tr') page.has_selector?(:xpath, '//table/tr') page.has_no_selector?(:content) page.has_xpath?('//table/tr') page.has_css?('table tr.foo') page.has_content?('foo')
RSpec のマッチャとして使うことも出来ます
page.should have_selector('table tr') page.should have_selector(:xpath, '//table/tr') page.should have_no_selector(:content) page.should have_xpath('//table/tr') page.should have_css('table tr.foo') page.should have_content('foo') page.should have_no_content('foo')
page_should_not have xpath よりも page.should have_no_xpath を使うべきです。詳しくは Ajax の章をご覧ください。
もしうまくいかない時は、page.html を使うと生HTMLを扱うことが出来ます。
page.html.should match /<span>.../i
Finding
フルのリファレンスはこちら: Capybara::Node::Finders
特定の要素を探して操作することが出来ます。
find_field('First Name').value find_link('Hello').visible? find_button('Send').click find(:xpath, "//table/tr").click find("#overlay").find("h1").click all('a').each { |a| a[:href] }
find は、Ajax の章で説明されているように、要素がページに表示されるのを待ちます。もし要素が現れなければエラーを吐きます。
これらの要素は Capybara の DSL を全て使え、ページの箇所を特定して DSL の作用する場所を制限することが出来ます。
find('#navigation').click_link('Home') find('#navigation').should have_button('Sign out')
Scoping
Capybara は form 操作やリンクやボタンのクリックなどの特定のアクションを、ページの特定のエリア内で行うように制限することが可能です。within メソッドを使うことでそれができ、オプションでセレクタの種類を特定することが出来ます。
within("li#employee") do fill_in 'Name', :with => 'Jimmy' end within(:xpath, "//li[@id='employee']") do fill_in 'Name', :with => 'Jimmy' end
fieldset や table 用の特別なメソッドがあります。fieldset は legend タグ内の id かテキスト、table は caption タグの id かテキストを見ます。
within_fieldset('Employee') do fill_in 'Name', :with => 'Jimmy' end within_table('Employee') do fill_in 'Name', :with => 'Jimmy' end
Scripting
ドライバがサポートしていれば、簡単に JavaScript を実行できます。
page.execute_script("$('body').empty()")
簡単な式であれば、script の結果を受け取ることが出来ます。複雑な式だとうまくいかないかもしれません。
result = page.evaluate_script('4 + 4');
Transactional fixture
Transactional Fixture はデフォルトの Rack::Test のみで使えますが、それ以外の Selenium 等のドライバでは使えません。Cucumber はそのあたり自動で面倒を見てくれますが、 Test::Unit や RSpec では、database_cleaner gem を使う必要があるかもしれません。詳しくはこのあたりを見てください。またはここかここに解決用のコードがあります。
Ajax
非同期の Javascript を扱う時に、まだページに存在していないされていないDOM要素を操作したい場面に出くわすことがあるかもしれません。Capybara はDOM要素が現れるのを待つことでうまく対処しています。
下記のような操作をしたとします。
click_link('foo') click_link('bar') page.should have_content('baz')
foo をクリックしたときに非同期の処理(例:Ajaxリクエスト)が走り、完了したら bar リンクがページに加えられるとした場合、bar がまだ出現していなければ bar のクリックで失敗します。しかし Capybara は短い時間で再度リンクを探します。次の行でも同じようにbazを探します。
待つ時間を変更することが出来ます。(デフォルトは2秒)
Capybara.default_wait_time = 5
この振る舞いがあるので、下記の二行は等価ではありません。常に have_no_xpath の方を使うようにしてください。
page.should_not have_xpath('a') page.should have_no_xpath('a')
page.should_not have_xpath('a') の方は非同期の操作を待ちません。
サポートしていないテストフレームワークで Capybara の DSL を使う
Capybara::DSL を include することで、どんなコンテキストでも Capybara のDSLを使うことが出来ます。
require 'capybara' require 'capybara/dsl' Capybara.default_driver = :culerity module MyModule include Capybara::DSL def login! within("//form[@id='session']") do fill_in 'Login', :with => 'user@example.com' fill_in 'Password', :with => 'password' end click_link 'Sign in' end end
リモートサーバを呼ぶ
通常、Capybara は Rack アプリをテストすることを期待します。しかし app_host を設定すると、インターネット上のウェブサーバと会話することができるようになります。
Capybara.current_driver = :selenium Capybara.app_host = 'http://www.google.com' ... visit('/')
デフォルトのドライバ(:rack_test)はリモートサーバをサポートしていません。サポートしているドライバでは、どんな URL でも直接 visit することが出来ます。
visit('http://www.google.com')
Capybara はデフォルトでは rack アプリを自動でブートしようとします。リモートアプリを使っていて Capybara の rack サーバをオフにしたい場合は下記のようにします
Capybara.run_server = false
session を手動で使う
Session を手動でインスタンス化して使うことが出来ます。
require 'capybara' session = Capybara::Session.new(:culerity, my_rack_app) session.within("//form[@id='session']") do session.fill_in 'Login', :with => 'user@example.com' session.fill_in 'Password', :with => 'password' end session.click_link 'Sign in'
XPath CSS and selectors
Capybara はデフォルトでは CSS を使います。セレクタから推測するようなことはしません。XPath を使いたい場合は、下記のようにすることで使えます
within(:xpath, '//ul/li') { ... } find(:xpath, '//ul/li').text find(:xpath, '//li[contains(.//a[@href = "#"]/text(), "foo")]').value
デフォルトのセレクタを XPath に変更することも出来ます。
Capybara.default_selector = :xpath find('//ul/li').text
Capybara はカスタムセレクタを作成して使うことも出来ます。同じような種類のセレクタをよく使う場合に役立ちます。
Capybara.add_selector(:id) do xpath { |id| XPath.descendant[XPath.attr(:id) == id.to_s] } end Capybara.add_selector(:row) do xpath { |num| ".//tbody/tr[#{num}]" } end
xpath メソッドに与えられたブロックは必ず XPath の表現を文字列もしくは XPath gem で作られたもので返さないといけません。上記のようにカスタムセレクタを設定した場合、下記のようにセレクタを使うことが出来ます。
find(:id, 'post_123') find(:row, 3)
XPath // の罠
XPath での // は特別で、あなたが思ってるような意味ではないかもしれません。通常の認識とは違い、// は "ドキュメント全体のどこか" であって "今のコンテキスト内でのどこか" ではありません。例えば
page.find(:xpath, '//body').all(:xpath, '//script')
これは全ての script タグを body の中から探すと思うかもしれません。でも実際には、これはドキュメント全体の script タグを探します。 .// は "カレントノードの配下" を表します。
page.find(:xpath, '//body').all(:xpath, './/script')
within で同じようにやるとこうなります
within(:xpath, '//body') do page.find(:xpath, './/script') within(:xpath, './/table/tbody') do ... end end
ドライバを設定、追加する
Capybara はドライバを切り替えることが簡単にできます。また、ドライバを設定するためのAPIが用意されているし、独自のドライバを追加することが出来ます。下記は selenium ドライバに chrome を使わせる方法です
Capybara.register_driver :selenium do |app| Capybara::Selenium::Driver.new(app, :browser => :chrome) end
違う名前を付けることで、ブラウザを使い分けることが簡単にできます。
Capybara.register_driver :selenium_chrome do |app| Capybara::Selenium::Driver.new(app, :browser => :chrome) end
ブロックの戻り値は Capybara::Driver::Base の記述する API に従うべきです。Capybara::Driver::Base を継承している必要はありません。gem はこの API を、自分独自のドライバを Capybara に加えるために使えます。
selenium wikiにはドライバ設定の情報が詳しく書かれています。
その他
- session と request にアクセスするのはテストからは無理です。response へのアクセスは限定されています。いくつかのドライバは response header と HTTP status code にアクセスできますが、アクセスできないドライバ(例: selenium)もあります。
- Rails の統合テストを使っていないので、Rails の特定のもの(例: controller)へアクセスすることは出来ません。
- 現在時刻に依存したフィーチャをうまく動かすためにモック化すると問題が起こることがあります。capybara の Ajax のタイミングは system の時間を使っているせいで、failure な時にCapybara はタイムアウトせずにハングってしまいます。時間を止める系のプラグインよりも、時間を移動させる系のプラグイン(例: timecop)でよく起こります。
- Rack::Test を使っているときは、URLで visit しているかどうかチェックするべきです。たとえば、session は posts_path と posts_url で別々になります。もし Action Mailer の中でURLを使っているとしたら、default_url_options を Rails のデフォルトの www.example.com に設定するべきです。