Rails + Turnip + Capybara + poltergeistでBDDテスト

ちょっとテストコードを見直す機会があったのでこの機会に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


```spec/turnip_helper.rb
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ファイル

```spec/features/hoge.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ファイル。

```spec/steps/hoge_steps.rb
# 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 へ](http://magazine.rubyist.net/?0042-FromCucumberToTurnip#l14)

[Ruby on Rails / Turnip / Capybara / Spork / poltergeist の利用設定](http://qiita.com/cuzic/items/56222e9ae1ecb4760592)