rspec-style-guide
rspec-style-guide copied to clipboard
Encourage to use local matchers
E.g.
matcher :be_just_like do |expected|
match {|actual| actual == expected}
end
Related docs: https://relishapp.com/rspec/rspec-expectations/v/3-8/docs/custom-matchers/define-a-custom-matcher#scoped-in-a-module https://relishapp.com/rspec/rspec-expectations/v/3-8/docs/custom-matchers/define-a-custom-matcher#scoped-in-an-example-group
Caveats
I personally find it very confusing that those matchers don't have access to group variables, as opposed to ad-hoc methods:
def send_message
have_attributes(channel: channel, text: text)
end
let(:channel) { '#announcements' }
let(:text) { 'Hello, world' }
it { is_expected.to send_message }
With a matcher:
let(:channel) { '#announcements' }
let(:text) { 'Hello, world' }
# works
matcher :send_message do |channel, text|
values_match? actual, have_attributes(channel: channel, text: text)
end
it { is_expected.to send_message(channel, text) }
# fails with
# undefined local variable or method `channel' for #<Class:#<RSpec::Matchers::DSL::Matcher:0x00007fb334216058>>
matcher :send_message do
values_match? actual, have_attributes(channel: channel, text: text)
end
it { is_expected.to send_message }
What does “local” mean in this context? Would you recommend defining one-off matchers for a single example group?
Yep, local to an example group, so that it's not accessible in the other example groups.
It depends on the number of examples in the group and the complexity of the match condition. Sometimes two examples are enough to start thinking of extracting them. Shared examples might be an option, but they create additional group, and with expensive setup this might not be desirable.
The matcher defined does not necessarily have to be defined in the example group itself, it may be in the module, and be included in the example group, or several example groups even spread over different spec files.