capybaraでユーザーテストを実行するときに気をつけること

capybara

jnicklas/capybaraを使用してユーザーテストを作成しようとしたのですが、よくドキュメントを読まずに検索しながら進めたところかなりはまりました。せっかくなのではまったところをまとめておこうと思います。

まず環境ですが、RailsRSpecを実行する際にcapybaraを使用している環境です。

  • Rails 4.1.1
  • ruby 2.1.1p76 (2014-02-24 revision 45161) [x86_64-darwin13.0]
  • Gem
    • settingslogic 2.0.9
    • capybara 2.2.1
    • rspec 2.14.1
    • poltergeist 1.5.1

今回のシナリオは plataformatec/devise の以下の機能についてテストを作成しました。

  1. ユーザーの新規作成画面でユーザーを新規に作成する。
  2. ログイン画面であらかじめ登録されたユーザーを使用してログインを行う。

それぞれはまりながら進んだこともあり、たったこれだけのことで8時間近くかかってしまいました…。

曖昧なタグが存在する(Capybara::Ambiguous: Ambiguous match, found 2 elements matching link or button "Sign up")

ユーザーの新規作成を行うときに以下のようなコードを記述しました。

[ruby] visit new_user_registration_path page.save_screenshot("screen_shot/the_signin_process/#{example.description}_before.png", full: true)

note:こちらはタグが曖昧なためエラーとなる

fill_in 'user_email', with: 'user@example.com' fill_in 'user_user_name', with: 'foobar' fill_in 'user_password', with: 'abcdefg123456' fill_in 'user_password_confirmation', with: 'abcdefg123456'

click_on sign_up [/ruby]

ところが、このボタン名にある 'Sign in' という文字列がヘッダ部分にも存在しておりどちらのタグを指定しているのかわからないということでエラーになりました。

実際には、binarylogic/settingslogic を使用していたときにエラーページの文字列定数を test モードには指定していなかったためエラー内容をつかむことができず時間がかかったということもありますが、、、

最終的には曖昧さを排除するために 'Sign in' ボタンが存在する form のidを指定することで対応を行いました。

[ruby] visit new_user_registration_path page.save_screenshot("screen_shot/the_signin_process/#{example.description}_before.png", full: true)

ここでformタグidを指定する

within('#new_user') do fill_in 'user_email', with: 'user@example.com' fill_in 'user_user_name', with: 'foobar' fill_in 'user_password', with: 'abcdefg123456' fill_in 'user_password_confirmation', with: 'abcdefg123456'

click_on sign_up end [/ruby]

URLを読むことができない(-> NameError: undefined local variable or method `new_user_registration_path' for main:Object)

前回のポストに書きましたが、URLを読むことができないということがありました。これはなかなかうまくいくまでに時間がかかったのですが StackOverflow を検索すればすぐに出てくることかもしれません。対応方法としては spec_helper に以下の内容を記述します。

[ruby] RSpec.configure do |config| ... 省略 ...

# RSpecでroutesのpathを指定するために設定 config.include Rails.application.routes.url_helpers

end [/ruby]

事前に作成したデータを参照できない

2のケースについてテストを作成する場合には、あらかじめログイン可能なユーザーを作成する必要があります。もちろん、1と2を同時にテストしても良いのでしょうが、テストの粒度として大きすぎるためそれぞれテストを行いたいと考えました。

その場合に、2のケースでは以下のようなテストになります。

  1. ユーザーを事前に作成する。
  2. 作成したユーザーでログインする。
  3. ログインが正しく実行される。

この手順で実際に作成したテストが以下のコードになります。

[ruby] require 'spec_helper'

describe 'ユーザーがログインをする' do

let(:password){'abcdefg123456'} let!(:login_user){ create(:user, {password: password})} let(:sign_in){ 'Sign in' }

context 'トップ画面に遷移し、ログインボタンをクリックしたとき' do before do visit root_path page.save_screenshot("screen_shot/the_signin_process/#{example.description}_before.png", full: true)

  within('#new_user') do
    fill_in 'user_email', with: login_user.email
    fill_in 'user_password', with: password

    click_on sign_in
  end

  page.save_screenshot("screen_shot/the_signin_process/#{example.description}_executed.png", full: true)
end

it '経費情報画面へ遷移していること' do
  expect(page.current_path).to eq root_path
  save_and_open_page
end

end end [/ruby]

このテストを実行すると、どうしても認証エラーとしてログイン画面へリダイレクトされてしまいました。

デバッガを使用して実際にdeviseの処理を追いかけたのですが、引数として設定された認証情報に該当するデータを取得できていないことがわかりました。いっぽうで RSpec の実行コンテキスト(beforeなど capybara が実行した以外の処理)では正常にユーザーが取得できていることが確認できました。

参照しているデータベースのデータが共有されていないということから、どうやら DatabaseCleaner/database_cleaner の設定が正しくないのではということに気づきました。

[ruby] RSpec.configure do |config|

config.before(:suite) do DatabaseCleaner.clean_with(:truncation) end

config.before(:each) do DatabaseCleaner.strategy = :transaction end

config.before(:each, :js => true) do DatabaseCleaner.strategy = :truncation end

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

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

end [/ruby]

この設定の場合、before で作成されたデータは トランザクション処理 されることになります。つまり、処理を開始したRSpecでは参照可能ですが、RSpecから起動された capybara のドライバはデータを参照することができないのです。

そこで、設定を変更して以下の通りトランザクションを使用するのではなくデータベースの内容を削除するように設定を変更しまし正常に動作するようになりました。

[ruby] RSpec.configure do |config|

config.before(:suite) do DatabaseCleaner.clean_with(:truncation) end

config.before(:each) do DatabaseCleaner.strategy = :truncation

# note:こちらは動作しない
# DatabaseCleaner.strategy = :transaction

end

config.before(:each, :js => true) do

DatabaseCleaner.strategy = :truncation

# note:こちらは動作しない
# DatabaseCleaner.strategy = :transaction

end

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

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

end [/ruby]

さいごに

いずれの情報もきちんとドキュメントを読んだり解説書を読めば記載されていることでした。片手間に導入しようとしてかえって時間がかかってしまった感じです。ということで、きちんとドキュメントは読みましょうということで。

ちなみに、capybara の使用方法については パーフェクト Ruby on Rails が参考になります。 Part3 に テストについて説明がされていますが、その中に capybara と poltergeist の使用方法について記載されています。今回のトラブルについてもよく読んでおけば回避できた内容でした…。