2010-03-22

Cucumber を PHP アプリに対して本当に使えることが分かった

何がきっかけかよく覚えていないんだけど、この数日、急に Cucumber のことを思い出したので試してみた。

Cucumber - Making BDD fun

Cucumber は一部ではとても有名な受け入れテストの記述&実行フレームワークとでも呼んだらいいのかな。有名な記事はこの辺。

まだあまり詳しく本家ドキュメントを読んでないんだけど、あちこちの記事から分かることは、これは

外部仕様あるいは要件定義を共有しやすくするフレームワーク

ということでしょうか。あたかも自然言語で書いたプレーンテキストがテストを動かすスクリプトとして機能しているように見えるので、今の動作で仕様を満たしています、あるいはここまでできています、ということを開発者間、開発者とマネージャ、プロダクトオーナーと共有しやすくなることが嬉しいみたい。

なんのこっちゃって感じですか?

機能的な部分をごく大雑把に言い直すとテスト用の DSL を提供して、そこからあれこれテストツールを呼ぶよ、って感じです。こっちの方が具体的な動作はイメージしやすいかな。

あとは

examples at master from aslakhellesoy's cucumber - GitHub

ここら辺のコードを眺めて動かしてみるとつかめると思う。

もちろん有名なのは Rails のテストなんだけど、例えば Ruby の Test::Unit と連携することもできるし、Webrat というツールを使うことで PHP アプリをテストすることも可能。

PHP アプリのサンプルがなかったので探したみたら

Cucumber+WebratでPHPアプリのテストをする | CAPH TECH

が見つかった。ただしどうもちょっと古いバージョンをもとにしているらしく、0.6.3 では動かなかった。Ruby 1.8.7 + Cucumber 0.6.3 で動かすにはこんな感じで修正が必要。

diff --git a/cucumber.yml b/cucumber.yml
index c39f416..c1869b5 100644
--- a/cucumber.yml
+++ b/cucumber.yml
@@ -1 +1 @@
-default: --language ja features
+default: features
diff --git a/features/show.feature b/features/show.feature
index e3afa77..3887cb7 100644
--- a/features/show.feature
+++ b/features/show.feature
@@ -1,3 +1,4 @@
+# language: ja
 機能: トップページの表示とサブミット

   シナリオ: トップページの表示
diff --git a/features/step_definitions/result_steps.rb b/features/step_definitions/result_steps.rb
index b22e762..85c2824 100644
--- a/features/step_definitions/result_steps.rb
+++ b/features/step_definitions/result_steps.rb
@@ -1,11 +1,11 @@
 # -*- encoding: UTF-8 -*-

 ならば /^"(.*)"と表示される$/ do |text|
-  response_body.to_s.force_encoding("UTF-8").should =~ /#{text}/m
+  response_body.to_s.should =~ /#{text}/m
 end

 ならば /^"(.*)"と表示されない$/ do |text|
-  response_body.to_s.force_encoding("UTF-8").should_not =~ /#{text}/m
+  response_body.to_s.should_not =~ /#{text}/m
 end

 ならば /^(\w+)メッセージが表示さる$/ do |message_type|
diff --git a/features/support/env.rb b/features/support/env.rb
index 54a9528..87f3521 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -1,5 +1,6 @@
 # RSpec
 require 'spec/expectations'
+require 'cucumber/formatter/unicode'

 # Webrat
 require 'webrat'
diff --git a/index.php b/index.php
index 5623672..bac22ea 100644
--- a/index.php
+++ b/index.php
@@ -9,7 +9,7 @@

     <?php if(!isset($_GET['text']) or empty($_GET['text'])) print '<p>テキストを入力してください。</p>'; ?>

-    <form action="/php_with_cucumber/index.php" method="get">
+    <form method="get">
         <p><textarea name="text" rows="10" cols="100"></textarea></p>
         <p><input type="submit" value="Submit" /></p>
     </form>

簡単に言うと

cucumber-0.5.0へ上げるときの注意点 - ヽ( ・∀・)ノくまくまー(2009-12-21)

を読めって話になります。読め。ただし、

require 'cucumber/formatter/unicode'

はちょっとした罠かも。example を読んでれば気づくけど。

あと以下の部分でテストするアプリの URL を決め打ちしてるので、対応が必要。

features/step_definitions/webrat_steps.rb
# -*- encoding: UTF-8 -*-

前提 /^(.*)ページを表示している$/ do |path|
 @response = visit "http://localhost/php_with_cucumber#{path}"
end

もし /^(.*)ページを表示する$/ do |path|
 @response = visit "http://localhost/php_with_cucumber#{path}"
end

もし /^"(.*)"ボタンをクリックする$/ do |button|

こういうのちょっとダサイよねぇ。開発環境、検証環境、本番環境の違いをもうちょっとスマートに書き分けたり、開発環境の URI が増えていっても対応が楽になるような工夫がないものかしら。1

感想

Webrat って Mechanize と何が違うの? と思ったら内部に Mechanize 用の adapter があって、実行時のレポートで Mechanize の警告が出たので、結局実際に外部のアプリにアクセスする部分は Mechanize を利用しているらしい。ここら辺、依存が深くなってきてちょっとややこしい。

また、Rails には plugin があるので Cucumber は使い始めやすいと思うんだけど、素の状態から使うのはちょっと面倒かなって感じがした。example レベルに留まっている step の記述がもっと充実してくれば違うんだろうけど、今のままだと

  1. プロダクトコード
  2. テストストーリー
  3. ストーリーを記述するための step

を書かなきゃいけないし、step はどうしても Ruby で書かざるを得ないのでプロダクトコードを別な言語で書いている場合はかなり面倒なことになってしまう。

ただし、その面倒をあえてやるべき場面もあって、それは内部の作り替えのとき。Cucumber で外側のテストがしっかり動かせていれば中の設計、もっと言えば中の環境はどうでもいいと言える。レガシーコードに悩んでいるアプリも、外を固めて差し替えてしまうことができるようになる。個人的には Mechanize はだいぶ慣れてきてるので、うまく使えれば動作の変化を最小限に抑えつつ古いコードを捨てるために使えるんじゃないかと考えている。

※ 学習曲線の初期段階においてにはちと荷が重い取り組みなので、試しに採用できそうな適切なサイズのアプリの見極めが大事になってきそう。自分の手もそれに集中させるのは難しいだろうし、古いアプリの仕様を後から書かなきゃいけないのも大変。現場は常に理想の正反対を行っているのだなぁ。でもそれがあるから新しいアイディアを思いつき、新しいツールが生まれてくるんだろうけど。

  1. そもそも本番環境を Cucumber でテストするっていう考え方がおかしいかもしれなので、必要ないかも。 

About

例によって個人のなんちゃらです