Cucumber + PHP

勉強のために github の Cucumber + PHP の設定方法に関するドキュメントを翻訳してみた。
PHP だけでなく Ruby 以外のどんな言語で作られたアプリケーションにも当てはまるので、よかったら参考にしてみてください。
原文:PHP - cucumber - GitHub


多くの人は rails をテストするために cucumber を使っているので、ウェブ上にあるハウツーやドキュメントのほとんども rails 向けに書かれている。しかし cucumber は素晴らしいツールなので、どんな種類のウェブアプリケーションでもテストできるし、それがどんな言語で作られていても構わない。Selenium を使えばよいハウツーが用意されていることも確かだが、遅いし設定方法がダサいので、役に立つのは javascript をテストする必要があるときぐらいだろう。BDD のストーリーを書き、cucumber で PHP のアプリをテストしたい場合にはどうすればよいだろう?

思ったよりやり方は簡単である。Webrat を使えば railsselenium または mechanize のどれかを使ってウェブアプリケーションとやりとりすることができる。なのでやることと言えば webrat が mechanize を使うように設定するぐらいで、あとはほとんどすべてがほかの cucumber のセットアップと同じ。

どうやればいいかはこんな感じ:

シナリオ: cucumber をインストールする
    前提 Ubuntu を使用していること
    かつ まだ cucumber をインストールしていないこと
    もし "apt-get install ruby ruby1.8-dev rdoc1.8 irb libxml2-dev libxslt1-dev libc6-dev-i386 libopenssl-ruby"を実行する
    # apt を使うと妙なパスにインストールしてしまうので、手動で rubygems をインストールする
    かつ "wget http://rubyforge.org/frs/download.php/60718/rubygems-1.3.5.tgz"を実行する
    かつ "tar xvf rubygems-1.3.5.tgz"を実行する
    かつ "cd rubygems-1.3.5"を実行する
    かつ "sudo ruby setup.rb"を実行する
    # github を gem のソースリストに加える
    かつ "gem sources -a http://gems.github.com"を実行する
    かつ "sudo gem install cucumber mechanize rspec webrat"を実行する
    ならば cucumber のインストールが完了していること

  シナリオ: PHP アプリ向けに cucumber の環境を設定する
    前提 動いている PHP アプリが存在すること
    かつ 現在位置がそのアプリのトップレベルのディレクトリであること
    もし "features"ディレクトリを作成する
    かつ "features/support"ディレクトリを作成する
    かつ "features/step_definitions"ディレクトリを作成する
    かつ 以下の行を"support/env.rb"に書く
# RSpec
require 'spec/expectations'

# Webrat
require 'webrat'

require 'test/unit/assertions'
World(Test::Unit::Assertions)

Webrat.configure do |config|
  config.mode = :mechanize
end

World do
  session = Webrat::Session.new
  session.extend(Webrat::Methods)
  session.extend(Webrat::Matchers)
  session
end
    ならば cucumber のテストを書き実行できること

その他いくつか気付いた点:

webrat は mechanize 使うように設定すると response.body の代わりに response_body を使うようだ。どうしてそうなのかはよくわからないが、http://github.com/brynary/webrat にある webrat のステップ定義を使う場合には、いくつかの置換をしないとうまく動かなかった。

もしテストのたびに同じテストデータを使いたいなら、いくつかの必須要件がある。まず、(テスト用の)データベースを用意してデータ内容を把握し十分にコントロールできるようにする必要がある。それから、テストを繰り返し実行するために、毎回同じデータを用意するべきだということである。*1

これを解決するためには、ちょっとハックしてシナリオの前と後に、データベースとテーブルのダンプを行えばよい。一番最初のテストを行う前にデータベースの状態を保存しておいて、最後に再読み込みし、次にテストを実行したときにもデータベースの中身は同じにするか、あるいは上記と同じことをフィーチャ毎・テーブル毎に繰り返すか。今まで試してきた中では、2 番目のアプローチのほうが速くて柔軟性もあるように思えた。

フィーチャ毎・テーブル毎のアプローチは hooks を使うと実現できる。以下に挙げるのは私が 'support/hooks.rb' の中に書いてあるコードである。

Before ('@reset_users') do
  # Save user table data
  save_table("game_user")
end

After ('@reset_users') do
  # Reload user table data
  load_table("game_user")
end

def save_table(table_name)
  run "mysqldump -u #{@@test_database_username} --password=#{@@test_database_password} #{@@test_database_name} #{table_name}> /tmp/#{table_name}"

end

def load_table(table_name)
  run "mysql -u #{@@test_database_username} --password=#{@@test_database_password} #{@@test_database_name} < /tmp/#{table_name}"
end

あとは @reset_users タグを user テーブルで何かするシナリオの前に書くだけ。そうすればシナリオが完了したあとに user テーブルをリセットしてくれるだろう。

  @reset_users
  Scenario: Create a user
    Given ...

PHP 向けの完全な env.rb はここにある。また、chits の features ディレクトリを見れば、実際にどうやって PHP アプリを cucumber でテストしているか見られるだろう。

*1:ここらへん原文のままだと少しわかりづらかったので意訳した