「be」と「eq」の違い、説明できる?RSpecマッチャ解説!

2024/12/23に公開

こんにちは!
ラブグラフエンジニアのひろです!

今回は、RSpec や Capybara でよく使われるマッチャについて解説していきます。
よく迷われがちな「be」と「eq」の違いにも触れていくので、ぜひ参考にしてみてください!

判定でよく使うマッチャ

expect(XXX).to eq

まずは、基本中の基本である 比較 のマッチャです。
eq は「XXXとYYYの が等しいかどうか」を判定します。
データベースのレコード数や、特定のオブジェクトの属性値をチェックするときによく使います。

例1: レコード数の確認

it "新しい本が作成される" do
  # create などで作成されたことを判定
  expect(Book.count).to eq 1
end

例2: 属性の確認

it "本のタイトルが更新される" do
  book = Book.create(name: "旧タイトル")
  book.update(name: "新タイトル")
  
  # update などで値が変わったことを判定
  expect(book.name).to eq "新タイトル"
end

eqbe の違い

ここで eq とよく似たマッチャである be について、違いを見てみましょう。

例: eqbe の違い

it "eq は成功し、 be だと失敗する例" do
  a = [1, 2, 3]
  b = [1, 2, 3]

  expect(a).to eq(b)  # 成功: 値が同じであればOK
  expect(a).to be(b)  # 失敗: a と b は異なるオブジェクト
end

RuboCop RSpec の推奨 (RSpec::BeEq ルール)

https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/BeEq
Rubocop の RSpec::BeEq というルールでは、true、false、nil の比較においては eq よりも be を使うことが推奨されています。
これは、be マッチャが 同一性 で比較するため、より厳密なテストが可能だからです。

  • 悪い例(eq を使用している)
expect(foo).to eq(true)
expect(foo).to eq(false)
expect(foo).to eq(nil)
  • 良い例(be を使用している)
expect(foo).to be(true)
expect(foo).to be(false)
expect(foo).to be(nil)

be マッチャはオブジェクトそのものが同じかどうかを比較するため、たまたま値が同じで、実際には違うオブジェクトである場合に、テストが誤って通ってしまうことを防ぐことができます。
なので、 true、false、nil 以外にも、be を使って確実な判定をするのが良いと考えています。


expect(XXX).to be_truthy / be_falsey

次は、 真偽値 を判定するためのマッチャです。
be_truthy は、Rubyにおける「真」になる全ての値 ( true オブジェクトなど) を受け入れます。
一方で、 be_falseynil または false のみを許容します。

例: Boolean値の判定

it "必要な情報が入力されていない場合は保存できない" do
  empty_book = Book.new
  expect(empty_book.save).to be_falsey  # 保存されない
end

expect { XXX }.to change { YYY }.from(xxx).to(yyy)

値の 変更前後を確認 できる便利なマッチャです。
例えば、ユーザーが予約をキャンセルした際に、そのステータスが正しく変化したことを検証する場合や、レコード数が正しく増減するかを確認するのに使います。

例1: 状態の変更を確認

it "注文がキャンセルされる" do
  order = Order.create(status: "active")
  
  expect { order.cancel! }.to change { order.status }.from("active").to("cancelled")
end

例2: レコード数の変化を確認

it "アクティブな注文数が減る" do
  expect { order.cancel! }.to change { Order.active.count }.from(10).to(9)
end

expect(page).to have_current_path

最後に紹介するのは、 Capybara を使った ページ遷移 の確認です。
have_current_path を使うことで、指定されたURLやパスに遷移しているかを確認できます。
特に create などの後に適切なページに遷移しているかどうかをテストする際に役立ちます。

例: ページ遷移の確認

it "本が作成され、本の一覧に遷移する" do
  visit new_admin_book_path

  fill_in "Title", with: "新しい本のタイトル"
  click_button "保存する"
  
  # ページ遷移したことを判定
  expect(page).to have_current_path admin_books_path
end

その他の便利なマッチャ

ここまで紹介した以外にも、以下のような便利なマッチャがあります。

  • expect(page).to have_content: ページ内に特定のテキストが含まれているか確認
expect(page).to have_content "作成が完了しました"
  • expect(page).to have_selector: ページ内に特定の要素が存在するか確認
expect(page).to have_selector "h1", text: "新しい本の作成"
  • expect { ... }.to raise_error: 特定のエラーが発生するか確認
expect { raise 'エラー' }.to raise_error(RuntimeError)

終わりに

今回ご紹介したマッチャは、 RSpec と Capybara を使ってテストを書く際に役立つものばかりです。
テストを書く際に、どのマッチャを使うかが明確だとテストの確実性と可読性が上がり、コードリーディングやレビューに使う時間を削減することもできますね。

ぜひ、日々のテストコード作成に役立ててください!

ラブグラフのエンジニアブログ

Discussion