RSpec マッチャー の使い方とカスタムマッチャー作成のベストプラクティス

ruby on rails

RSpec マッチャー を使うことで、テストコードの可読性を高め、効率的に動作確認ができます。本記事では、基本のマッチャーから応用テクニックまで、実践例を交えながら徹底解説します。


基本のマッチャー

比較系マッチャー

eq, eql, equal, be の違い

  • eq: 値が等しい
  • eql: 値と型が等しい
  • equal, be: オブジェクトIDが同じ
describe '比較系マッチャーの例' do
  it 'eq と eql の違い' do
    expect(1).to eq(1)          # 値が等しい
    expect(1).to eql(1)         # 値と型が等しい
    expect(1).not_to equal(1.0) # オブジェクトIDが異なる
  end
end

真偽値系マッチャー

be_truthy, be_falsey, be_nil の使い分け

  • be_truthy: nil 以外の全ての値
  • be_falsey: nil または false
  • be_nil: nil のみ
describe '真偽値系マッチャーの例' do
  it 'be_truthy と be_falsey' do
    expect(true).to be_truthy
    expect(false).to be_falsey
    expect(nil).to be_falsey
  end
end

コレクション系マッチャー

配列の検証

include, match_array, contain_exactly の使い分け

describe 'コレクション系マッチャーの例' do
  it 'include の例' do
    array = [1, 2, 3]
    expect(array).to include(1)
    expect(array).to include(1, 3)
  end
end

Rails特有のマッチャー

ActiveRecord関連

RailsにはActiveRecordモデルを効率よくテストするためのマッチャーが用意されています。
以下は バリデーションスコープアソシエーション のテスト例です。

バリデーションのテスト

バリデーションのテストには be_valid を使用しますが、より詳細なテストには validate_presence_ofvalidate_uniqueness_of を使います。

describe User, type: :model do
  it '名前、メールアドレスがある場合、有効であること' do
    user = User.new(name: 'Alice', email: 'test@example.com')
    expect(user).to be_valid
  end

  it '名前がない場合、無効であること' do
    user = User.new(name: nil, email: 'test@example.com')
    expect(user).not_to be_valid
    expect(user.errors[:name]).to include("can't be blank")
  end

  it 'メールアドレスの一意性を検証する' do
    User.create!(name: 'Bob', email: 'unique@example.com')
    duplicate_user = User.new(name: 'Alice', email: 'unique@example.com')
    expect(duplicate_user).not_to be_valid
  end
end

スコープのテスト

Railsのスコープ (scope) をテストする場合、match_array を使って期待する配列の順序を無視して比較できます。

describe User, type: :model do
  before do
    @active_user = User.create!(name: 'Active', active: true)
    @inactive_user = User.create!(name: 'Inactive', active: false)
  end

  it 'active スコープは有効なユーザーのみを返すこと' do
    expect(User.active).to match_array([@active_user])
  end
end

アソシエーションのテスト

Railsのアソシエーション (belongs_to, has_many など) をテストするには、shoulda-matchers を活用します。

describe User, type: :model do
  it { should have_many(:posts) }
  it { should belong_to(:company) }
end

カスタムマッチャーの作成

RSpecには用意されていない オリジナルの検証ロジック を追加する場合、カスタムマッチャーを作成できます。
これにより、共通の検証を使い回す ことが可能です。

数値の偶数チェック (シンプルな例)

RSpec::Matchers.define :be_even do
  match do |actual|
    actual.even?
  end

  failure_message do |actual|
    "expected #{actual} to be even"
  end
end

describe 'カスタムマッチャーの例' do
  it '偶数の検証' do
    expect(4).to be_even
    expect(3).not_to be_even
  end
end

複雑なカスタムマッチャー (レスポンスのJSON構造検証)

RSpec::Matchers.define :have_json_key do |expected_key|
  match do |response|
    json = JSON.parse(response.body)
    json.key?(expected_key)
  end

  failure_message do |response|
    "expected JSON response to have key '#{expected_key}'"
  end
end

describe 'APIレスポンスのテスト' do
  it 'name キーを含むこと' do
    get '/api/v1/users/1'
    expect(response).to have_json_key('name')
  end
end

マッチャー活用のベストプラクティス

マッチャーを効率的に使うことで、テストの 読みやすさ保守性パフォーマンス を向上させられます。

読みやすさ重視のテスト設計

  • テストの意図が明確に伝わるように、マッチャーは具体的な名前を使う
  • expect ブロックを1つのテスト内に 1つだけ 記述することで、失敗箇所を特定しやすくする

カスタムマッチャーの共通化

  • 複数のテストで同じ検証を使う場合、カスタムマッチャーにまとめることで 重複を排除
  • RSpecの support ディレクトリ を活用して、共通化したカスタムマッチャーを読み込む

パフォーマンスの考慮

  • 不要なクエリ発行を防ぐために、 include や joins をテスト対象のスコープに含める
  • before ブロックを活用 して、重複するセットアップを最適化

この記事を通して、RSpecのマッチャーをより深く理解し、テストの品質と効率を大幅に向上させる方法を身につけましょう!

まとめ

マッチャーを理解することで、テストの品質と効率が向上します。RSpecの強力な機能をフル活用して、より良いテストを書きましょう。

コメント

タイトルとURLをコピーしました