🕌

RSpec で任意のメソッドが呼ばれたかどうかをテストする

2024/02/07に公開

テストを書くときに『内部で任意のメソッドが呼ばれていること』を担保したい事があります。
そういう場合は RSpec では receive マッチャで検証する事ができます。

class User < Struct.new(:name)
  def exists?
    find!(name)
  end

  def find!(name)
    # ...
  end
end

require "rspec"

RSpec.describe User do
  describe "#exists?" do
    subject { user.exists? }

    let(:user) { User.new(name: "homu") }

    it do
      # it 内で user,find!("homu") が呼び出されていることを担保するテストになる
      # with でメソッドの引数を指定する事ができる
      expect(user).to receive(:find!).with(user.name)
      subject
    end

    # これは失敗する
    it do
      expect(user).to receive(:find!).with(user.name)
      # user.find! が呼び出されない
      # subject
    end

    # これは失敗する
    it do
      expect(user).to receive(:find!).with("mami")
      # user.find! は呼び出されているが意図する引数ではない
      subject
    end

    # これは成功する
    it do
      expect(user).to receive(:find!).with(user.name)
      # subject や User#exists? 経由で呼び出されている必要はない
      user.find!("homu")
    end
  end
end

こんな感じで『レシーバに対して任意のメソッドが呼び出されているかどうか』を検証するテストを記述することができます。
内部の実装に依存する場合はこういうテストを書きたいときもありますねー。

GitHubで編集を提案

Discussion