MeWrite Docs

RSpec: expectation not met

RSpecでエクスペクテーションが満たされなかった場合のエラー

概要

RSpecでエクスペクテーション(expect)が期待した結果と一致しなかった場合に発生するエラーです。

エラーメッセージ

Failure/Error: expect(result).to eq(5)

  expected: 5
       got: 4

  (compared using ==)

または

Failure/Error: expect(service).to have_received(:send_email).once

  expected: 1 time with any arguments
  received: 0 times with any arguments

原因

  1. 値の不一致: 期待値と実際の値が異なる
  2. モックの設定ミス: スタブやモックが正しく設定されていない
  3. 非同期処理: 処理完了前にアサーション
  4. コンテキスト: テスト間で状態が共有されている

解決策

1. 基本的なマッチャー

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
RSpec.describe Calculator do
  describe '#add' do
    it 'adds two numbers' do
      calculator = Calculator.new

      expect(calculator.add(2, 3)).to eq(5)
    end
  end
end

# よく使うマッチャー
expect(value).to eq(expected)        # 等値
expect(value).to be(expected)        # 同一オブジェクト
expect(value).to be_truthy           # 真
expect(value).to be_falsy            # 偽
expect(value).to be_nil              # nil
expect(value).to include('text')     # 含む
expect(array).to contain_exactly(1, 2, 3)  # 順不同で一致

2. モックとスタブ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
RSpec.describe UserService do
  let(:repository) { instance_double(UserRepository) }
  let(:service) { described_class.new(repository) }

  describe '#create_user' do
    it 'saves the user' do
      user = instance_double(User)

      # スタブ(戻り値を設定)
      allow(repository).to receive(:save).and_return(true)

      result = service.create_user('John')

      # モック(呼び出しを検証)
      expect(repository).to have_received(:save).with(kind_of(User))
    end
  end
end

3. 呼び出し回数の検証

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 1回
expect(mock).to have_received(:method).once

# 複数回
expect(mock).to have_received(:method).exactly(3).times

# 1回以上
expect(mock).to have_received(:method).at_least(:once)

# 呼ばれていない
expect(mock).not_to have_received(:method)

4. 引数のマッチング

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 完全一致
expect(service).to have_received(:find).with(1)

# 任意の引数
expect(service).to have_received(:find).with(anything)

# 型チェック
expect(service).to have_received(:save).with(kind_of(User))

# ハッシュの部分マッチ
expect(service).to have_received(:update).with(hash_including(name: 'John'))

# ブロックで検証
expect(service).to have_received(:process) do |arg|
  expect(arg.name).to eq('John')
end

5. 例外のテスト

1
2
3
4
5
6
7
8
it 'raises an error' do
  expect { service.divide(1, 0) }.to raise_error(ZeroDivisionError)
end

it 'raises an error with message' do
  expect { service.validate('') }
    .to raise_error(ArgumentError, /cannot be empty/)
end

6. 変化のテスト

1
2
3
4
5
6
7
8
9
it 'increases the count' do
  expect { service.add_item }
    .to change { Item.count }.by(1)
end

it 'changes the status' do
  expect { user.activate! }
    .to change(user, :status).from('pending').to('active')
end

7. 共有コンテキスト

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
RSpec.shared_context 'with authenticated user' do
  let(:user) { create(:user) }
  before { sign_in(user) }
end

RSpec.describe 'Dashboard', type: :feature do
  include_context 'with authenticated user'

  it 'shows user name' do
    visit dashboard_path
    expect(page).to have_content(user.name)
  end
end

8. 共有サンプル

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
RSpec.shared_examples 'a collection' do
  it 'responds to each' do
    expect(subject).to respond_to(:each)
  end

  it 'responds to size' do
    expect(subject).to respond_to(:size)
  end
end

RSpec.describe Array do
  it_behaves_like 'a collection'
end

9. before/after フック

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
RSpec.describe UserService do
  before(:each) do
    # 各テスト前に実行
    DatabaseCleaner.start
  end

  after(:each) do
    # 各テスト後に実行
    DatabaseCleaner.clean
  end

  around(:each) do |example|
    # テストを囲む
    Timecop.freeze(Time.local(2025, 1, 1)) do
      example.run
    end
  end
end

10. 非同期テスト

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Capybara
it 'shows success message', js: true do
  click_button 'Save'

  # 自動的にリトライ
  expect(page).to have_content('Saved successfully')
end

# カスタム待機
it 'processes in background' do
  service.process_async

  expect { service.status }.to eventually eq('completed')
end

よくある間違い

  • expectto の間にドットを忘れる
  • let で定義した値がメモ化されることを忘れる(let! で即時評価)
  • allowexpect(...).to have_received の順序
  • instance_double で存在しないメソッドをスタブしようとする

最終更新: 2025-12-09