「be」と「eq」の違い、説明できる?RSpecマッチャ解説!
こんにちは!
ラブグラフエンジニアのひろです!
今回は、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
eq
と be
の違い
ここで eq
とよく似たマッチャである be
について、違いを見てみましょう。
-
eq
: これは値の等価性を確認するためのマッチャです。オブジェクトが同じでなくても、値が同じであればeq
は成功します。
https://www.rubydoc.info/gems/rspec-expectations/RSpec%2FMatchers:eq -
be
: こちらは、オブジェクトの 同一性 を確認します。つまり、 同じオブジェクトであるか (オブジェクトIDが一致するか)を判定します。
https://www.rubydoc.info/gems/rspec-expectations/RSpec%2FMatchers:be
eq
と be
の違い
例: 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 ルール)
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_falsey
は nil
または 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