Ruby Freaks Lounge

第21回Railsアプリの受け入れテストをCucumberで書こう

はじめに

Cucumberとは受け入れテストのためのテスティングフレームワークです。CucumberはRuby on Railsに依存しているライブラリではないため、例えば同じRuby制のフレームワークであるSinatraはもちろん、PHPなどで書かれたアプリケーションでも使用することができます。

Sinatraやフレームワークを使用していない素のRubyスクリプトなどをベースにCucumberの解説をすることも可能ですが、今回は仕事で使っている人が多く、また筆者自身もRailsを使って開発をしていることもあって、Railsをベースに解説させていただきます。

なぜCucumberなのか

筆者が勤めている株式会社RAWHIDE.では、Railsアプリを作成する場合、原則的にCucumberでテストを書くようにしています。Cucumber採用当時は、社内にナレッジが少ない、不慣れなど、なかなか気合いを入れないとついつい書かないで済ませることもありましたが、今ではアプリケーションの動作を100%網羅できているとまではいきませんが、それなりの部分をカバーできるようになりました。

そもそもなぜCucumberなのかと言いますと、弊社ではテスティングフレームワークにはRSpecを採用しています。今ではCucumberと併用して使っていますが、以前はRSpecのみでした。RSpecではModel、View、Controllerのテストを書いていましたが、基本的にはユニットテストなので、あるModel単体のテスト、あるController単体のテストとなるのが普通です。

RSpecでユニットテストを書き始めた当時は、TDDという手法でテストを書いていました。これはこれで十分に効果があったのですが、結局はユニットテストなのでアプリケーションの動作を保証することにはなりません。つまり、顧客に対して「このアプリケーションは要求仕様を満たしています」と言うことはRSpecだけでは無理だったのです。

では、結局のところチェックリストを用意して、手動でチェックするしかないのかと色々考えているときにCucumberの存在を知りました。結果、今ではCucumberでアプリケーションの動作をテストすることができ、顧客に対しても自信を持って要求仕様を満たしていると言えるようになりました。

また、後述するようにCucumberのテストコードは比較的自然語彙に近い形で記述できます。自然語彙に近い形でテストコードを記述することで、ユーザストーリーに近い形ででテストを記述することができます。つまり、顧客の要望をほぼそのままテストコードに落とすことができます。顧客の要望に近い形でテストコードを書くことで、開発側と顧客との間でアプリケーションの仕様を共有しやすくなります。

Cucumberの仕組み

少々、前振りが長くなってしまいましたが、具体的にCucumberがどういったものか説明したいと思います。Cucumberではプレーンテキストでテストコードを記述します。

例えば、ログイン部分のテストを記述する場合は以下のようになります。

もし "トップ"ページを表示している
かつ "メールアドレス"に"hogehoge@hoge.com"と入力
かつ "パスワード"に"hogehoge"と入力
かつ "ログイン"ボタンをクリックする

ならば "ようこそ!ほげほげさん"と表示されていること

どういった処理をテストしているのか一目でわかると思います。このテストコードを実行できるのがCucumberです。

ですが、上記のテストコードはプレーンテキストですので、当然このままでは実行することができません。Cucumberはまずこのテストコードを実行できる形に解析を行います。その仕組みをstepsと言います。

stepsの定義は、以下のように記述されています。

when(/^"([^\"]*)"ページを表示する$/ do |page|
  visit path_to page
end

When /^"([^\"]*)"に"([^\"]*)"と入力する$/ do |field, value|
  fills_in(field, :with => value)
end

When /^"([^\"]*)"ボタンをクリックする$/ do |button|
  click_button(button)
end

Then /^"([^\"]*)"と表示されていること$/ do |text|
  response.body.should =~ /#{Regexp.escape(text)}/m
end

Cucumberは実行したいテストコードにマッチするstepsを探し、そこに記述されている処理を実行します。

「例えば、先程の例で言えば「もし "トップ"ページを表示している」「/^"([^\"]*)"ページを表示する$/」のstepsにマッチするので、そのstepsのブロックの処理を実行します。

最終的にそのテストコードが正しく実行できたかどうかの判断はThenの処理の結果で判断します。そのためこの場合は「ならば "ようこそ!ほげほげさん"と表示されていること」「^"([^\"]*)"と表示されていること$」とマッチするので、そのstepsの処理の結果が Trueであればテストコードが正しく実行できたということになります。

RSpecとの兼ね合い

以前、⁠CucumberとRSpecの2つのテスティングフレームワークのうちどちらを使うべきか?」と質問を受けたことがあります。筆者の答えは「両方使う」でした。どういうことかと言うとCucumberとRSpecではテストするレイヤーが違います。ざっくり言うと内部仕様をRSpecで、外部仕様をCucumberでテストすると言ったところでしょうか。

実際の開発の流れにそって説明しますと、まずは顧客から受けた要件をCucumberのテストコードに落しこみます。最終的なゴールをまず用意しておくのです。その後はModelのテストをRSpecで書き、そのテストが正しく実行できるプロダクトコードを記述していきます。

次に、ControllerとViewのテストもRSpecで書き、そのテストが正しく実行できるようプロダクトコードを書きます。この時点でModel、Controller、ViewとRSpecで記述したそれらのテストコードが出来ています。

大抵の場合、この時点でCucumberのテストも通っていることがほとんどなので、顧客の要件が実装できたと言うことになります。もしCucumberのテストが通らない場合、どこかで仕様通りに実装できていない部分があると言うことなので、もう一度Modelから見直します。開発は基本的にこの繰り返しです。

まとめ

Cucumberの仕組みをざっくりと説明しましたが、なんとなく感じは掴めたでしょうか。この手のテスティングフレームワークは、なんだかんだでとりあえず「使ってみる」というのが重要です。

次回は、実際にRailsアプリに対してCucumberでテストを書いてみたいと思います。

おすすめ記事

記事・ニュース一覧