👀

RSpecのdefine_negated_matcherが便利だった

2022/11/13に公開

RSpecで肯定と否定を混ぜた合成式を使いたい場合は、RSpec::Matchers.define_negated_matcherが便利という話です。

通常、RSpecで「マッチャ合成式(Compound Matcher Expressions)」を使うとき、肯定形と否定形は同時には使えません。マッチャ合成式とは、andorで複数の期待値を確かめる書き方です。

describe 'サンプルテスト' do
  it 'aで始まり、oで終わること' do
    user = User.new(name: 'taro')
    expect(user.name).to start_with('t').and end_with('o')
  end
end

(サンプルのテストなので細かいところは気にせず)

このように、肯定and肯定はOKです。しかし、not_toと組み合わせて使うことはできません。

it 'aで始まらず、oで終わること' do
  user = User.new(name: 'taro')
+ expect(user.name).not_to start_with('a').and end_with('o')
end

「その書き方はサポートされてないけど、代わりにRSpec::Matchers.define_negated_matcher使ってみたら?」と提案してくれます。

Failure/Error: expect(user.name).not_to start_with('a').and end_with('o')
     
NotImplementedError:
  `expect(...).not_to matcher.and matcher` is not supported, since it creates a bit of an ambiguity. Instead, define negated versions of whatever matchers you wish to negate with `RSpec::Matchers.define_negated_matcher` and use `expect(...).to matcher.and matcher`.

このような場合、自分で否定形のマッチャを定義できます。spec/rails_helper.rbに次のように記述します。

spec/rails_helper.rb
RSpec::Matchers.define_negated_matcher :not_start_with, :start_with

start_withの否定形をnot_start_withとして定義しています。

すると、さきほどのテストを次のように書くことができます。

it 'aで始まらず、oで終わること' do
  user = User.new(name: 'taro')
+ expect(user.name).to not_start_with('a').and end_with('o')
end
Finished in 0.01318 seconds (files took 0.7407 seconds to load)
1 example, 0 failures

仕事でdefine_negated_matcherを目にする機会があり、便利だと思ったため記事にしました。

肯定と否定を混ぜた合成式を使いたい場合は、RSpec::Matchers.define_negated_matcherが便利です。

参考

Discussion