ちょっとテストコードを見直す機会があったのでこの機会にTurnipを入れてGherkinで記述する事でより、第三者が見やすいようにしてみた。
その際に色々調べたのでメモ。
###1 Turnipってなんぞや?
簡単にいうと、
・大枠はCucumberと同じ
・Cucumberの際に一番ネックとなっていた正規表現の部分をよりプレースホルダーを使って簡単に記述
・Capybaraの開発者が作ったものなので、Capybaraとの相性は申し分なし
###2 Gherkin書式ってなんぞや?
・ Cucumber により拡張された書式。Business Readable DSL(≒仕様記述のDSL)といわれている
・システムの振る舞いを記述するための自然言語に近い書式
(下記の実際のコードを見てもらえれば理解できるかと思います)
※ちなみに...有名な他のDSLとしは、
Rspec :テストのDSL
Chef :システムアドミニストレーションのDSL
Fluentd:マシン間配送のDSL
Capybara: e2e検証のDSL
###3 導入
####1 インストール
group :developmentm :test do
gem "turnip"
end
bundle install
####2 設定ファイル
今回はcapybaraのdriverとしてpoltergeistを使ってます。
※poltergeistのインストールは下記を参考にしていただければw
http://d.hatena.ne.jp/ria10/20130122/1358836316
http://qiita.com/katryo/items/e33197a4fa26de42a3c4
require 'turnip'
require 'turnip/capybara'
require 'turnip/rspec'
require 'capybara'
require 'capybara/poltergeist'
#各種driverの設定
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, {:js_errors => false, :default_wait_time => 30, :timeout => 100})
end
#mobileのuseragent
Capybara.register_driver :mobile do |app|
Capybara::RackTest::Driver.new(app,
:headers => {'HTTP_USER_AGENT' => '※適宜好きなものを'})
end
#pcのuseragent
Capybara.register_driver :pc do |app|
Capybara::RackTest::Driver.new(app,
:headers => {'HTTP_USER_AGENT' => '※適宜好きなものを'})
end
Capybara.configure do |config|
config.default_driver = :pc
config.javascript_driver = :poltergeist
config.ignore_hidden_elements = true
config.default_wait_time = 30
end
Dir.glob("spec/**/*steps.rb") { |f| load f, true }
####3 テストを書いていく
turnipでは、
・spec/features/*.featureという機能を記述するファイル
→機能 (とストーリー)、シナリオ、ステップの 3 階層で記述します
・/spec/steps/*.steps.rbというテストシナリオで実施する具体的な手続きを記述するファイル
という大きく2つの構成で成り立ちます。
・featureファイル
# encoding: utf-8
# language: ja
@common
機能: hoge機能
hoge機能のテストを行っています。
hoge、fugaの両パターンにてテストしてます。
背景:
前提 PCを利用している
@hoge
シナリオ: topを訪問しhogehogeすると成功
もし topを訪問
かつ hogehogeボタンをクリック
ならば hoge成功
@fuga
シナリオ: topを訪問しhogehogeすると成功
もし helpを訪問
かつ fugafugaをフォームに入力
ならば hoge成功
最初から説明していくと、
まず今回は日本語で記述しているので、下記を記述する必要があります。
# language: ja
大きくfeatureファイルを構成するものとしては下記4つで、
・機能
→'hoge機能'
・背景(※なくても問題ない)
→'前提 PCを利用している'
・ストーリー(※なくても問題ない)
→'hoge機能のテストを行っています。hoge、fugaの両パターンにてテストしてます。'
・シナリオ
→'topを訪問しhogehogeすると成功'
・ステップ
→' もし〜...ならば 〜'
上記のソースで当てはめるとこのようになります。
@common @hoge @fuga
これはタグと言われるものです。
あとでsteps.rbを見ていただけると分かりますが、
steps_for :common do
@common が付いているシナリオのみに適用
end
てな感じで、特定のものを適用させたい際につけるようです。
覚えるfeatureで使うstepの予約語としては下記ぐらいかと。
日本語 | 英語 |
---|---|
前提 | Given |
もし | When |
ならば | Then |
かつ | And |
ちなみに、「前提」、「もし」、「ならば」は同じ挙動のようですw
・stepファイル
featureファイルに対応するのがstepファイル。
# encoding: utf-8
steps_for :common do
step ':page_name を訪問' do |page_name|
url = "http://127.0.0.1/#{page_name}"
visit url
end
step 'hoge成功' do
expect(page).to have_content("hoge成功")
end
end
steps_for : hoge do
step 'hogehogeボタンをクリック' do
find('button#hogehoge').click
end
end
steps_for : fuga do
step 'fugafugaをフォームに入力' do
fill_in 'fugafuga_form', with: 'fugafuga'
end
end
まずそれぞれのシナリオに共通するものはタグで共有化させてしまって、各々で必要なものだけ別に書くような感じ。
例えば、全てのシナリオで使いそうなものに関してはGlobal stepというものを用いて定義しておく。(例:ログイン機能とか)
また汎用的に出来るものはプレースホルダーを使って実装。
step ':page_name を訪問' do |page_name|
end
moduleを使った方が良いものに関しては、
https://github.com/jnicklas/turnip/blob/master/examples/steps/alignment_steps.rb
このようにmoduleを使って実装する。
###4 詰まったところ
①1featureファイルに2シナリオ以上記載するとなぜかjavascriptのdriverが選ばれてないとエラーが出た。。。
→背景の部分で前提を定義しておき、シナリオ毎に呼び出して対応(下記のような形)
step 'PCを利用している' do
Capybara.current_driver = :poltergeist
end
②同じstep名を定義できないので、すぐに重複してうのではという疑問があった。
→基本的なものは共通ファイルに定義して、各々で使うものはsteps_forで定義して使えば問題なさそう。
###5 最後に...
個人的には、良いと思った点は
・すごく読みやすい
→別のメンバーが実装したテストも、どういうシナリオで何を行っているのかが分かるので理解しやすい
・ドキュメント的な位置づけにもなりそう
→駆け出しサービスは特に仕様をまとめたドキュメントなど作ってないと思うので、Gherkinでテストを書いておけば、ドキュメントの位置づけにもなるかと
###6 参考
[jnicklas / turnip]
(https://github.com/jnicklas/turnip)
エンドツーエンドテストの自動化は Cucumber から Turnip へ
Ruby on Rails / Turnip / Capybara / Spork / poltergeist の利用設定