くりにっき

フルスタックキュアエンジニアです

RSpecでstringの比較をする時はbe_includeではなくincludeを使った方がいい

仕事中にRSpecでちょっとした発見があったので忘れないうちにブログに残しておきます

spec

describe String do
  it "match with be_include" do
    "cure peace".should be_include "azatoi"
  end

  it "match with include" do
    "cure peace".should include "azatoi"
  end
end

この2つは両方とも特定の文字列が含まれるかどうかのテストですが、エラーメッセージが全く違います

be_includeのテスト結果

  it "match with be_include" do
    "cure peace".should be_include "azatoi"
  end

1) String match with be_include
Failure/Error: "cure peace".should be_include "azatoi"
expected include?("azatoi") to return true, got false

includeのテスト結果

  it "match with include" do
    "cure peace".should include "azatoi"
  end

2) String match with include
Failure/Error: "cure peace".should include "azatoi"
expected "cure peace" to include "azatoi"

理由

名前が似ている be_include と include ですが、中身は全く違います

be_include

RSpecでは

some_instance.should be_xxxx

のような呼び出しがあった時は

some_instance.xxxx?

という呼び出しに変わり、これを評価した結果がtrueじゃなかったらテストが失敗します。

今回の場合は String#include? が呼ばれます。
RSpecにはtrueかfalseしか入ってきていないためエラーメッセージでもtrueかfalseしかチェックしていないことが分かります。

1) String match with be_include
Failure/Error: "cure peace".should be_include "azatoi"
expected include?("azatoi") to return true, got false

Test::Unitで置き換えると

assert( "cure peace".include?("azatoi") )

みたいな感じです。

include

Spec::Matchers#include が呼ばれます。

文字列がmatcherに渡されているため、エラーメッセージにも実測値と期待値の両方が出ています。

2) String match with include
Failure/Error: "cure peace".should include "azatoi"
expected "cure peace" to include "azatoi"

結論

テストする内容が同じならエラーメッセージが分かりやすい(情報量が多い)方を選んだ方がいいです

余談

JUnitのテストで

String actual = "〜";
assertTrue( actual.contains(expected) );

なんて書いている人は今すぐJUnit実践入門を買いましょう(ステマ)

gist

The RSpec Book (Professional Ruby Series)

The RSpec Book (Professional Ruby Series)

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)