orangain flavor

じっくりコトコト煮込んだみかん2。知らないことを知りたい。

Ruby製アプリじゃなくてもTurnipで自動受け入れテストがしたい

Python製のWebアプリケーションの自動受け入れテストをしたくて、調べてみました。

Ruby界隈だと、最近ではCucumberに代わってTurnipというツールが流行っているみたいなので試してみました。


(c) .foto project

自動受け入れテスト用のライブラリ

Rubyで自動受け入れテストをしようとすると、以下の3つの選択肢があるようです。

Cucumberは以前にも使ったことがあるので、今回はTurnipを選択してみました。詳しい説明は以下のページに譲ります。

Webページを自動操作するライブラリ

Webページの自動操作にはCapybaraを使います。デフォルトではRackTestというドライバが使われますが、これはHTTP通信を行わないドライバでありRackアプリケーションでしか使えません。

このためHTTP通信を行う他のドライバを使います。ヘッドレスでJavaScriptを利用できるドライバとして、以下の2種類があるようです。

最近ではPoltergeistのほうがインストールが簡単かつ高速で人気らしいので、今回はPoltergeistを使いました。

前提

前提として、Ruby 2.1*1 およびBundlerがインストールされているものとします。

PoltergeistのインストールにはPhantomJSが必要なので、 PoltergeistのREADMEに従ってインストールします。

Macならば brew install phantomjs で簡単にインストール出来ました。

準備

適当なフォルダを作ります。

$ mkdir bus-spec
$ cd bus-spec

以下のコマンドでGemfileを作ります。

$ bundle init

Gemfileを以下のように編集します。

source "https://rubygems.org"

gem 'turnip'
gem 'capybara'
gem 'poltergeist'

以下のコマンドでGemをインストールします。

$ bundle install --path=vendor/bundle

TurnipのREADMEを参考に、テストに必要なファイル・フォルダを作成します。

$ mkdir -p spec/{features,steps}

.rspecを以下の内容で作成します。

-r turnip/rspec

spec/spec_helper.rbを以下の内容で作成します。

Dir.glob('spec/steps/*steps.rb') { |f| load f, true }

require 'capybara/rspec'
require 'capybara/poltergeist'

Capybara.default_driver = :poltergeist

# 以下の関数はGeolocation APIをシミュレートするヘルパー関数です。
def simulate_location(latitude, longitude, accuracy=0)
  Capybara.current_session.execute_script <<-EOS
    window.navigator.geolocation = {
      getCurrentPosition: function(success) {
        var position = {coords: {latitude: #{latitude}, longitude: #{longitude}, accuracy: #{accuracy}}};
        success(position);
      }
    };
  EOS
end

rspecを実行して以下のように表示されれば準備完了です。

$ bundle exec rspec
No examples found.


Finished in 0.00005 seconds
0 examples, 0 failures

Turnipによる受け入れテスト

それではTurnipによる自動受け入れテストを書いていきます。

spec/features/search.featureには、プログラムに詳しくない人が読んでもわかる書式(Gherkin形式)で機能を記述します。

# encoding: utf-8
# language: ja

機能:PCまたはスマートフォンでバス停を検索
     シナリオ:トップページにアクセスして検索する
          前提ポケドスにアクセスする
          前提"京都市役所"に居る
          もしトップページを表示する
          ならば画面に"近くのバス停を探す"ボタンが表示されていること
          もし"近くのバス停を探す"ボタンをクリックする
          ならば画面に"この近くのバス停"と表示されていること
          かつ画面に"京都市役所前: 91m"と表示されていること

プレースホルダーに日本語を使う場合は、""で囲うなど少し工夫が必要です。参考:Ruby - Turnip で placeholder のマッチングを柔軟にする - Qiita

spec/steps/bus_steps.rbには、上で書いたGherkin形式のステップ(「前提」「もし」「ならば」など)に対応するプログラムを書きます。

# encoding: utf-8

step 'ポケドスにアクセスする' do
  Capybara.app_host = 'http://bus.capybala.com/'
end

step ':placeに居る' do |place|
  case place
  when '京都市役所'
    @place = {lat: 35.01129870069897, lng: 135.76807713296512}
  else
    raise "Unknown place"
  end
end

step 'トップページを表示する' do
  visit '/'
  simulate_location(@place[:lat], @place[:lng]) if @place
end

step '画面に:labelボタンが表示されていること' do |label|
  expect(page).to have_link(label)
end

step ':labelボタンをクリックする' do |label|
  click_link label
end 

step '画面に:textと表示されていること' do |text|
  expect(page).to have_content(text)
end

再度rspecを実行して以下のように表示されれば成功です。

$ bundle exec rspec
.

Finished in 2.92 seconds
1 example, 0 failures

まとめ

Turnipはこのように、テストする機能を自然文で書けるテストライブラリであり、Capybaraと組み合わせることでWebサービスの自動受け入れテストを行えます。

2012年に書いた記事と比べると、Cucumber → Turnip、capybara-mechanize/capybara-webkit → Poltergeistと変化しました。

正直Rubyをゴリゴリ書いていない身としては、TurnipのCucumberに対する利点として挙げられる、RSpecが使える点とプレースホルダーが簡単に書ける点が、それほど嬉しいことなのかよくわかりませんでした。

また、CucumberやTurnipのGherkin書式は、普通のテストを書くのに比べてコストがかかる割に、どの程度受け入れられるの?という考えもあります。 参考:RSpecで、Cucumberのような結合試験のspecを記述する - 高尾宏治日記 on はてな

うまく使えば強力なツールですが、使いドコロをよく考えて使う必要があるでしょう。

ここで説明したものをもう少し複雑にしたコードをGitHubで公開しています。

参考サイト・書籍

*1:今回は2.1を使いましたが、1.9.3以上ぐらいで動くと思います。