RSpecを使ってみた感想

RSpecを使う前は、assert_equalがshouldになっただけでTest::Unitと同じだろ?と思っていたのだが、使ってみると全く別物だと感じた。その違いは何かというと

  • Test::Unitは、メソッド毎に、各状態における振る舞いを確認するもの
  • RSpceは、ある状態における各メソッドの挙動を確認するもの

テストの単位をメソッドとするか、オブジェクトの状態とするかの違いがあるようだ。

例えばTest::Unitでは、メソッド単位でワンセットのテストコードを書く。例えば、test_to_s というテストメソッドで、to_sメソッドに関する挙動をチェックする。

class AtomFeedTest < Test::Unit::TestCase
  #
  # to_sメソッドの挙動をテスト
  #
  def test_to_s
    
    # feedの空要素を出力
    feed = Atom::Feed.new
    assert_equal "<feed/>", feed.to_s 
    
    # id をセットすると、id を子要素として出力
    feed = Atom::Feed.new
    feed.id = "ほげ"
    assert_equal "<feed><id>ほげ</id></feed>", feed.to_s 
  end
end

一方、RSpceでは特定のオブジェクトの状態をテスト単位する。以下の例はオブジェクトをnewした直後、何も操作していない状態における各メソッドの振る舞いを検査するテストだ。

describe Atom::Feed, "をnewした直後に、" do
  before do
    @feed = Atom::Feed.new
  end

  it "to_sすると、空要素のfeedタグを出力する" do
    @feed.to_s.should == "<feed/>"
  end

  it "to_rexmlすると、feed空要素がルートノードのREXML::Documentを生成" do
    @feed.to_rexml.should be_instance_of REXML::Document
    @feed.to_rexml.to_s.should == 
      "<?xml version='1.0' encoding='UTF-8'?>" +
      "<feed xmlns='http://www.w3.org/2005/Atom'/>"
  end  
end

Test::Unitのテストコードように、同じメソッドを状態を変化させつつ連続で呼び出すという事は実際のコードではまずない。それよりもRSpecでするように、ある特定の状態の時に色々なメソッドを呼ぶ事が多い。だから、オブジェクトの状態に注目し、その状態における各メソッドの振る舞いが想定通りであるか調べるRSpecがより実践的であるように思える。もちろんTest::Unitでも同じ事は可能だが、そのために設計されたツールを使う方が簡単だ。

RSpecは、その名前のとおり自然と仕様書のような記述がされるように設計されている。面白いと思ったのはエラーメッセージ。例えば先ほどのRSpecでテスト失敗すると

Atom::Feed をnewした直後に、to_sすると、空要素のfeedタグを出力する FAILED

のような出力がされるので、何がどのように仕様を満たしていないのか分かる。エラー時に自然な日本語となるようなメッセージを考えていくと、自然に確認すべき項目が埋まっていく。

新しいメソッドを追加する時のように、状態単位ではなくメソッド単位で振る舞いを調べたい場合もある。そんな時は

describe Atom::Feed, "のpublishedプロパティは、" do
  before(:all) do
    @atom = Atom::Feed.new
  end
  
  it "最初はnilで、" do
    @atom.published.should be_nil
  end
  
  it "Timeインスタンスをセットできて、" do
    @atom.published = Time.local(2008,10,3,12,15,30)
    @atom.published.strftime("%Y/%m/%d %H:%M:%S").should == "2008/10/03 12:15:30"
  end
  
  it "iso8601の日付文字列もセットできる。" do
    @atom.published = "2009-03-31T10:12:00+09:00"
    @atom.published.strftime("%Y/%m/%d %H:%M:%S").should == "2009/03/31 10:12:00"
  end
end

と、書くといいかも。

itが浮いてしまうが、日本語でメッセージを書くのだから気にする必要はないと思う。