RSpec shared_examples conditionally check for block presence

Viewed 270

Problem: I am attempting to create a shared example that has a series of base tests and conditionally can run a few more tests if a let was declared in the block.

Background: I have a response object that is used as the return value for service objects. It has a payload, which by default is an empty hash but it can contain data if the service sets them.

Using a shared example, I can easily cover the base case when a payload is not present. However, some services populate the payload. Given that the payload often references variables defined by let statements, I opted to share the context through a block instead of passing in parameters to the shared context.

Here is what I would like to do, I have mimicked the behavior with doubles so that this snippet is valid and can be used to test. The subject double represents the service object, and execute returns a double representing the response object.

RSpec.describe 'conditional shared_examples' do
  shared_examples_for 'a successful Service' do
    let(:response) { subject.execute }

    it 'returns a success ServiceResponse' do
      expect(response.success?).to be(true)
    end

    it 'contains the payload from the service', if: payload do
      expect(response.payload).to eq(payload)
    end
  end

  describe 'base shared_examples without a block' do
    subject { double(execute: double(success?: true)) }

    it_behaves_like 'a successful Service'
  end

  describe 'when a block is provided' do
    subject { double(execute: double(success?: true, payload: { foo: 'bar' })) }

    it_behaves_like 'a successful Service' do
      let(:payload) { { foo: 'bar' } }
    end
  end
end

This fails because payload is not available for the conditional check, here is the error:

NameError:
  undefined local variable or method `payload'

This makes sense to me, payload is not available at the ExampleGroup level, it is only available from within an example.

Questions:

Is there a way to check if a block was passed or if a method has been defined through let at the ExampleGroup level?

How can I conditionally check if this it_behaves_like was passed a block or has a specific let method declared?

0 Answers
Related