Rubyのテストには、RSpecもいいけど、Shouldaが良いよ。ってことで、
達人プログラマのDavid ThomasがShouldaを分かりやすく紹介してくれていたので、仮訳してみたよ。
(翻訳・公開することを快く承諾してくれました。)
英語が苦手なので間違いもあると思います。指摘してくれると嬉しいです。
オリジナルの記事:Shoulda used this earlier
—– ここから —–
Shoulda(もっと早く使っていれば)
色んな意味で、ソフトウェアをテストすることは、外に出て運動をすることに似ている。しなきゃいけないことは分かってるし、運動は自分を良い状態にしてくれるのは分かってるんだけど、それをやらない言い訳を考えるのもすごく簡単だ(明日、今日の分もやるよ)。
だから、テストをやりやすくするものは何だって良いことだ。だって、やらない言い訳を減らしてくれるからね。
昔ながらのxUnit-styleのテスティングフレームワークについて、絶対好きになれないことのひとつは、セットアップとティアダウンの構造だ。これらのフレームワークでは、テストケースはひとつのクラスで、セットアップとティアダウンはそのクラスのメソッドとして実装される。ひとつひとつのテストもメソッドだ、だから基本的な流れは、こんな風になる。
for each test method in the class
run setup
run the test method
run teardown
end
ナイス・アンド・シンプル。すべてのテストメソッドは、セットアップメソッドによって作られた標準環境の恩恵を受ける。そして、ティアダウンメソッドは、後で片付けの仕事をしてくれる。
こんなとき以外は・・・私がテストを書くときは、私はいつもたくさんのシナリオをセットアップしたくなる。A and B and Cをやりたい。それから、A and Bだけどnot Cの場合。それから、A and not B。それから、A and D。などなど。たくさんのテストケースのクラスを書くにはふたつの方法がある。共通のセットアップの実行を継承したサブクラスを使う方法、または、テストメソッドごとにセットアップコードを書く方法だ(多くの場合、ヘルパーに取り出す)。結局、私はほとんどいつも後者をやる。そしてそれは退屈で、しかも、セットアップコードのおかげで、それぞれのテストが見にくくなってしまう。
実は、RSpecに浮気したんだ。specフレームワークが私の求めているものを持っているみたいだったから。だけど、RSpecを使うのは楽しめなかった。(猫好きか、犬好きか、みたいなものだと思う。)
そこで、Shouldaですよ。
そんなとき、2週間くらい前だけど、Mike ClarkとChad Fowlerがshouldaを紹介してくれたんだ。Shouldaはテストのフレームワークじゃない。そのかわり、Shouldaは、RubyのTest::Unitフレームワークを、テストコンテキスト(文脈)の考え方で拡張してくれるんだ。ひとつのコンテキストは、共通点を持っているテストメソッドを集めたテストケースの一部分だ。最も簡単な場合、コンテキストは、単に注釈のための道具として使える(うん。これはくだらない例だけど):
context "My factorial method" do
should "return 1 when passed 0" do
assert_equal 1, fact(0)
end
should "return 1 when passed 1" do
assert_equal 1, fact(1)
end
should "return 6 when passed 3" do
assert_equal 6, fact(3)
end
end
単にセットアップブロックを書くだけで、コンテキストの中のものは共通のセットアップコードをシェア(共有)することができる。
class CartTest < Test::Unit::TestCase
context "An empty cart" do
setup do
@cart = orders(:wilmas_empty_cart)
end
should "have no line items" do
assert_equal 0, @cart.line_items.size
end
should "have a zero price" do
assert_equal 0, @cart.price
end
end
context "Some other context..." ...
end
end
という訳で、ひとつのテストケースの中で、複数のコンテキストをセットアップできるし、それぞれのコンテキストは、固有の環境を持つことができる。
待てよ、私の元々の問題に戻ろう。私はよく、関連するテスト環境のヒエラルキー(階層)をセットアップしたくなるんだった。Shouldaのコードはこれを素晴らしいやり方で処理してくれる、なぜならコンテキストをネストさせてくれるからだ。例えば、支払い時に、クレジットカードの処理で、住所間違いで弾かれ、その後、住所を修正して承認された場合、何か追加の情報を顧客に提供する機能だ。私は2つのテストをしたい。最初のアドレス間違いがなかった場合とアドレス間違いがあった場合だ。
この環境をセットアップするために、ショッピングカートをセットアップする必要がある。支払いへのダミーの応答をつくらなきゃいけない、それからアプリケーションへのレスポンスをpostしなきゃいけない。最初のアドレスが間違っていた場合、応答を生成するより前に、その注文に関する処理に、その間違い項目の注入もしたい。
shouldaを使えば、簡単にネストされたコンテキストを作ることができた。最上位のコンテキストは共用のセットアップを行う。内側のコンテキストは、その後、そのコンテキスト内のテストのために適切な環境のセットアップをおこなう。こんな風に:
context "Checking out" do
setup do
@cart = cart_named(:freds_full_cart)
@cart.prepare_for_store_authorize!
@params = approved_authnet_response(@cart)
end
context "with no AVS errors in CC transaction history" do
setup do
post :post_from_authnet_authorize, @params
end
should_redirect_to "{:action => :receipt}"
end
context "with AVS errors in CC transaction history" do
setup do
avs_error = CcTransaction.new(:response_code => 2, :response_reason_code => 27)
@cart.cc_transactions < :explain_avs_mismatch}"
end
end
外側のセットアップは、内側のコンテキストのどの実行よりも前に実行される。そして、内側のコンテキストのセットアップは、そのコンテキストを実行する時に、実行される。そして、Shouldaは、それをすべて追跡しているので、アサーションが失敗したときには、とても自然なエラーメッセージを得ることができる。例えば、上の2番目のコンテキストの中のテストが失敗した場合は、次のようなエラーメッセージが得られる。
Checking out with AVS errors in CC transaction history should
redirect to "{:action => :explain_avs_mixsmatch}".
という訳で、私はやっとテスト環境のヒエラルキーをセットアップすることができるようになった。画期的って訳じゃない。テストしない言い訳を減らすひとつに過ぎないけれど・・・
—– ここまで —–
その他、Rubyでのテストについて、役立ちそうな記事へのリンクを置いておきます:
スはスペックのス〜RSpec(関西弁)の動画 by 角谷HTML化計画
RSpecよりShoulda、fixturesよりヘルパーとMocha by Unexplored Rails
shouldaをインストールしてみる by んばぁってなんなのよ