Madogiwa Blog

主に技術系の学習メモに使っていきます。

Ruby: RSpecのカスタムマッチャを作る方法MEMO📝

RSpecのカスタムマッチャの作り方をいろいろ調べたのでメモ📝

作り方

RSpec::Matchers.defineを使う

カジュアルな追加方法としてRSpec::Matchers.defineで追加する方法があります。 以下の公式ドキュメントの通りにRSpec::Matchers.defineの引数にmatcher名を渡してdefineのブロック引数に期待する値、matchの引数に実際の値を指定してあげれば良いようです📝

You can create such a matcher like so:

RSpec::Matchers.define :be_in_zone do |zone|
  match do |player|
    player.in_zone?(zone)
 end
end

Module: RSpec::Matchers — Documentation for rspec-expectations (3.12.3)

実際に以下のような単純に同一の値になるか確認するカスタムマッチャを追加して、

RSpec::Matchers.define :my_eq do |expected|
  match do |actual|
    expected == actual
  end
end

以下のようなテストを流すと、

describe 'test' do
  it 'check my_eq' do
    expect(true).to my_eq(false)
  end
end

以下のようにテスト結果が表示されます🙌

Failures:

  1) test check my_eq
     Failure/Error: expect(true).to my_eq(false)
       expected true to my eq false

moduleで定義する

RSpec::Matchers.defineを使うとカジュアルに追加できますが実際にマッチャのテストコードを書きたいといった場合にはmoduleとして定義するとやりやすいようです。

以下のような検証用のロジックをもつclassとマッチャの呼び出すテストコード内で呼び出す名称のメソッドを持つmoduleを定義してあげて、

module MyEqMatcher
  class Matcher
    def initialize(expected)
      @expected = expected
    end

    def matches?(_expected)
      @actual = actual
      @expected == @actual
    end

    def failure_message
      "#{@expected} expected, but got #{@actual}"
    end
  end

  def my_eq(expected)
    Matcher.new(expected)
  end
end

以下の通りにconfig.includeでテストコード内で利用できるようにすることができます。

RSpec.configure do |config|
  config.include MyEqMatcher
end

以下の通り実行するといい感じにカスタムマッチャーを使ってエラーになることが確認できます 🙌

describe 'test' do
  it 'check my_eq' do
    expect(true).to my_eq(false)
  end
end
  1) test check my_eq
     Failure/Error: expect(true).to my_eq(false)
       false expected, but got true

おわりに

RSpecのカスタムマッチャ、意外と簡単に追加できて便利ですね!!

参考

semaphoreci.com

tabakazu.hateblo.jp

thoughtbot.com