rubocop / rspec-style-guide

Best practices for writing your specs!
http://rspec.rubystyle.guide
956 stars 118 forks source link

Encourage to use local matchers #62

Open pirj opened 5 years ago

pirj commented 5 years ago

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 }
bquorning commented 5 years ago

What does “local” mean in this context? Would you recommend defining one-off matchers for a single example group?

pirj commented 5 years ago

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.