Skip to content

🆕 RuboCop rule: Prevent hook/let definitions within `each` in RSpec

Problem

In RSpec you can write code like this:

# Bad
RSpec.describe 'Foo' do

[true, false].each do |value|
  before do
    @value = value
  end

  it 'works' do
    expect(@value).to eq(value)
  end
end
Failure
Foo
  # order random
  works (FAILED - 1)
  works

Failures:

  1) Foo works
     Failure/Error: expect(@value).to eq(value)

       expected: true
            got: false

       (compared using ==)

       Diff:
       @@ -1 +1 @@
       -true
       +false

     # ./spec/models/foo_spec.rb:12:in `block (3 levels) in <top (required)>'
     # ./spec/support/system_exit_detected.rb:7:in `block (2 levels) in <top (required)>'

Finished in 0.08224 seconds (files took 3.22 seconds to load)
2 examples, 1 failure

Failed examples:

rspec ./spec/models/foo_spec.rb[1:1] # Foo works

Randomized with seed 41211

This code sample above will fail but it's might not always be the case. See the #previous-discussion below.

There are several ways to fix this issue above.

Use context

# Good
RSpec.describe 'Foo' do
  [true, false].each do |value|
    context "with #{value}" do
      before do
        @value = value
      end

      it 'works' do
        expect(@value).to eq(value)
      end
    end
  end
end

Use shared_examples

RSpec.describe 'Foo' do
  shared_examples 'shared' do |value|
    context "with #{value}" do
      before do
        @value = value
      end

      it 'works' do
        expect(@value).to eq(value)
      end
    end
  end

  it_behaves_like 'shared', true
  it_behaves_like 'shared', false
end

Use RSpec::Parameterized

# Good
RSpec.describe 'Foo' do
  before do
    @value = value
  end

  where(:value) { [true, false] }

  with_them do
    it 'works' do
      expect(@value).to eq(value)
    end
  end
end

Proposed solution

Create a 👮 rule to flag use of .each which directly (without extra context/describe) defines hooks like before/after/around.

Link to yet to be created development guidelines in GitLab.org / GitLab in the offense message.

Previous discussion

The following discussion from gitlab-org/gitlab!99026 (closed) should be addressed:

Edited by Peter Leitzen