Rspec-core: let! should evaluate prior to any before

Created on 27 Jul 2015  路  1Comment  路  Source: rspec/rspec-core

I have a request spec describing an endpoint behavior. In the root context, I have:

before { patch '/api/v2/users/meberships', request.to_json, auth_headers(current_user) }

One of my test contexts let! creates a credential making current_user an owner.

When the test runs, the before executes prior to the let! and fails.

I can get the behavior I want by touching the credential in the before block, so the let! is behaving like let.

RSpec.describe URI('/api/v2/users/'), type: :request do
  let(:current_user) { Fabricate(:user) }
  let(:org) { Fabricate(:organzantion) }

  context 'Failing PATCH to /memberships' do
    let(:request) { {} }

    before do
      patch '/api/v2/users/meberships', request.to_json, auth_headers(current_user)
    end

    shared_examples 'a valid response' do
      specify { controller.should respond_with :created }
    end

    context 'as an organization owner' do
      let!(:credential) { Fabricate(:credential, user: current_user, organization: org, role: :owner) }

      it_should_behave_like 'a valid response' # fails because credential hasn't been fabricated
    end
  end

  context 'Working PATCH to /memberships' do
    let(:request) { {} }
    let(:credential) {}

    before do
      credential # late bound
      patch '/api/v2/users/meberships', request.to_json, auth_headers(current_user)
    end

    shared_examples 'a valid response' do
      specify { controller.should respond_with :created }
    end

    context 'as an organization owner' do
      let!(:credential) { Fabricate(:credential, user: current_user, organization: org, role: :owner) }

      it_should_behave_like 'a valid response' # succeeds 
    end
  end
end

Most helpful comment

In general, before and let! run in declaration order, e.g.:

before ( puts "before" }
let!(:foo) { puts "let!" }

Would print:

let!

...whereas this:

let!(:foo) { puts "let!" }
before ( puts "before" }

...would print:

let!
before

...so you are in complete control of when let! and before get executed.

In your case, that doesn't actually help, because your let! declaration is nested more deeply relative to the before. RSpec _always_ evaluates things outer context to inner context. This is the way things have always worked, and supporting running things from the inside out would be very confusing.

In general, I recommend against:

  • Using let! when the exact order in the setup it runs is important. When ordering is important, it's far better to be _explicit_ about it, and do the actions in sequential order in a single block by sequentially calling the appropriate methods. Otherwise you risk a future refactoring to your spec subtlety changing the order and either causing your spec to fail, or worse -- turning it into a false positive.
  • Putting the "act" part in a before block. before is intended to help with common setup needs (the "arrange" part of the arrange/act/assert pattern). Using before for the "act" part (e.g. making the HTTP request) causes this problem where you've got a particular example that needs some additional setup before the "act" but is prevented from doing so by the fact that the "act" has been put in a before hook. For better to just extract a useful helper method (e.g. make_request) and calling that at the appropriate places in your examples.

>All comments

In general, before and let! run in declaration order, e.g.:

before ( puts "before" }
let!(:foo) { puts "let!" }

Would print:

let!

...whereas this:

let!(:foo) { puts "let!" }
before ( puts "before" }

...would print:

let!
before

...so you are in complete control of when let! and before get executed.

In your case, that doesn't actually help, because your let! declaration is nested more deeply relative to the before. RSpec _always_ evaluates things outer context to inner context. This is the way things have always worked, and supporting running things from the inside out would be very confusing.

In general, I recommend against:

  • Using let! when the exact order in the setup it runs is important. When ordering is important, it's far better to be _explicit_ about it, and do the actions in sequential order in a single block by sequentially calling the appropriate methods. Otherwise you risk a future refactoring to your spec subtlety changing the order and either causing your spec to fail, or worse -- turning it into a false positive.
  • Putting the "act" part in a before block. before is intended to help with common setup needs (the "arrange" part of the arrange/act/assert pattern). Using before for the "act" part (e.g. making the HTTP request) causes this problem where you've got a particular example that needs some additional setup before the "act" but is prevented from doing so by the fact that the "act" has been put in a before hook. For better to just extract a useful helper method (e.g. make_request) and calling that at the appropriate places in your examples.
Was this page helpful?
0 / 5 - 0 ratings