RSepcをつかったModelとControllerの基本的な(ユニット)テストは、
なんとか書けるようになってきましたが、
「ちゃんとテストやれてる!」
と言えるにはまだまだ、
- インテグレーションテスト
- エンドツーエンドテスト
- 受け入れテスト
色々とやれることはあります。
直近、ユーザーのアクションがより重要なサービスの開発を進めることもあり、
「エンドツーエンドテスト」をやれる良さ気な方法は何か調べてみて、動くまでをまとめてみした。
※結論からいうと、「RSpec ☓ Capybara ☓ Poltergeist」で書くことにした記事です。
目次
過去記事
※RSpecの最初のセットアップやModel Controllerユニットテストの
書き方例は下記にまとめてみましたのでよかったら。
E2Eテストに何が必要?
シナリオ/テストコード
- シナリオが書ける
- シナリオを実行して確認するテストコードが書ける
→Cucumber, Turnip, RSpec feature spec
画面操作
- フォームやボタンを操作できる
- 表示されている画面要素が正しいか確認できる
- 画面遷移が確認できる
→ Capybara
js動作
- js動作環境でテストコードが実行できる
→ selenium, Capybara-webkit, Poltergeist 等
- js動作環境でのテストをheadlessで高速にできるようにする
→ Capybara-webkit, Poltergeist 等
デバッグ
- デバッグのためのキャプチャ保存
→ launchy(Capybaraの依存関係に含まれる), poltergeistのスクリーンショット保存
etc..
シナリオ(ステップ)を書くのはどれがいい?
ユーザーのシナリオごとにテストコードを書いていくには、色んなライブラリもあったりするようだったので、ざっくり調べてみました。
Cucumber
日本語でシナリをを書いて行ける。それを元に実際のテストプログラムfileは別で作成。
変化が激しく、学習コストが高い様子。
Turnip
Cucumberの改善としてだされたらしいので、日本語シナリオ使いたいと思ったら、こちらのほうが今は良さそうです。
こちらも日本語でシナリオを書くファイルと、ステップごとに実際のテスコードを書くファイルと分けて書くようです。
※参考
Rubyist Magazine - エンドツーエンドテストの自動化は Cucumber から Turnip へ
RSpecの feature specのみ
上記のような日本語のシナリオだけをまとめることはせず、
単純にシナリオごとにテストコードを書いていくようです。
結論
今関わっているプロジェクトでは、非エンジニア以外にテストシナリオを
説明することはあまりないので、普通にRSpecのfeature specで書いていこうとおもいます^^;
js動作のドライブはどれがいいの?
selenium vs Capybara-webkit vs Poltergeist
jsが動く環境での確認をしたいだけなら、seleniumだけで良いようですが、
調べていくと、Poltergeist
が良さそうでした。
-
seleniumは、
→ブラウザを必要とするため、テストに時間がかかる -
Capybara-webkitは、
→Qt, xvfb が必要
→準備にちょっと色々めんどうそうなのがある -
Poltergeistは、
→phantomjs を使う
→phantomjs入れて gemに書くだけで簡単そう
参考
poltergeist - Capybaraを使う際に知っておきたいこと - Qiita
E2Eの環境セットアップ方法は?
上記をふまえて、
「RSpec ☓ Capybara ☓ Poltergeist」が使えるまでを設定してみました。
Capybara セットアップ
rollback の仕方が変わるそうなので、database_cleanerの設定が必須となるようです。
Gemfile
group :test do
gem 'database_cleaner', '~> 1.3.0' # テスト実行後にDBをクリア
gem 'capybara', '~> 2.4.3' # ブラウザでの操作をシミュレートしてテストができる
rails_helper.rb
require 'capybara/rails'
require 'capybara/rspec'
RSpec.configure do |config|
config.use_transactional_fixtures = false #trueでいける方法もあるようです
# 省略
# テスト実行後にDBをクリア
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation,{:except => %w{categories jobs initials job_positions}})
DatabaseCleaner.strategy = :transaction
end
config.before :each do
DatabaseCleaner[:mongoid].start
DatabaseCleaner.start
end
config.after :each do
DatabaseCleaner[:mongoid].clean
DatabaseCleaner.clean
end
自分は、mongoとmysqlを使っているので上記のように書きました。
他DatabaseCleanerについて詳しくは、下記を
DatabaseCleaner/database_cleaner
deviseでのログイン対応
ログイン機能の実装には、お馴染みの devise
を使っています。
ログインユーザーを想定して、Capybaraをつかった操作をするには、追加で設定が必要です。
設定追加
RSpec.configure do |config|
config.include Warden::Test::Helpers
# 省略
config.before(:suite) do
Warden.test_mode!
# 省略
end
config.after :each do
Warden.test_reset!
# 省略
end
spec コードの中で、下記のように書くとログインしたことになる
feature "UserActions", :type => :feature do
context "ログインしている場合" do
before(:each){
# devise login
user = create(:user)
login_as(user, :scope => :user)
}
※参考
How To: Test with Capybara · plataformatec/devise Wiki
Poltergeist セットアップ
phantomjs をインストールします
自分の場合は、vagrant CentOSに入れたかったので下記を実行しました
$ wget https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-1.9.8-linux-x86_64.tar.bz2
$ tar xvjf phantomjs-1.9.8-linux-x86_64.tar.bz2
$ cd phantomjs-1.9.8-linux-x86_64
$ sudo ln -sf "$(pwd)"/bin/phantomjs /usr/local/bin/phantomjs
Gemfile
gem "poltergeist" # headlessでjsテストをする(phantomjsが必要)
rails_helper.rb
require 'capybara/poltergeist'
Capybara.javascript_driver = :poltergeist
あとは、feature specのscenario のoptionにjs: true
をつければjsが動作する&headlessな環境でテスト出来ます。
※参考
feature specの書き方は?
サンプル
require 'rails_helper'
feature "UserActions", :type => :feature do
context "ログインしている場合" do
before(:each){
# devise login
user = create(:user)
login_as(user, :scope => :user)
}
scenario "#btn1ボタンを押すと「詳細ページ」から「作成ページ」へ遷移できる" do
# 詳細ページへ
item = create(:item)
visit item_path(item.seq)
# ボタンクリック
find('#btn1').click
expect(current_path).to eq '/items/new'
end
end
context "ログインしていない場合" do
scenario "#btn1ボタンを押すとログインモーダルが立ち上がる", js: true do
# 詳細ページへ
item = create(:item)
visit item_path(item.seq)
# ボタンクリック
find('#btn1').click
expect(page).to have_css "div.remodal-wrapper.remodal-is-opened"
end
scenario "jsが有効でない場合、#btn1ボタンを押してもremodalが立ち上がらない" do
# 詳細ページへ
item = create(:item)
visit item_path(item.seq)
# ボタンクリック
find('#btn1').click
expect(page).not_to have_css "div.remodal-wrapper.remodal-is-opened"
end
end
end
実行
$ rspec spec/features/user_actions_spec.rb
簡単な補足/注意点
- jsが動作する環境でのテストは、
js: true
とします - pageでCapybaraをつかっていろんな情報が取得できるので
binding.pry
をして
ls page
とすると色々便利なメソッドが用意されていることが分かるとおもいます - Capybaraで、対象のセレクタを指定するときに、同名のClassがあるものを指定するとエラーになります。
Cabybara書き方参考
Cabybaraでは、こんな時どう書くのか、まとめられた記事がいくつかあって助かりましたm(_ _)m
デバッグする
Launchyのおかげで、下記の一行を、確認したい位置(specコード内)に追記すると、
その時点のhtmlをtmp/capybara
に保存してくれます。
save_and_open_page
poltergeistのスクリーンショットを保存する機能を使うこともできるようです
page.save_screenshot('/path/to/file.png')
参考
poltergeist - Capybaraを使う際に知っておきたいこと - Qiita
おわりに
まだ自分は、エンジニアに本業を絞って2年ですが、
いいシステムを作る&安定した運用には、テストは重要ではないかなと思ってます。
。。そう思いつつ、普段、忙しさに追われたり、テストを書くとはどういうことなのか、
模索しているところではありますが、^^;
目の前に囚われ過ぎない、後のことも考えたシステム作りの文化に貢献できる
エンジニアになってけるようさらに精進してきたいと思います。
2016年も頑張るぞ(*´ω`)ノ