RSpec: Let statements sometimes an issue as I need their values to exist in the database when I perform a get request

Viewed 85

Suppose I have the following RSpec request tests:

RSpec.describe Api::OrdersController, type: :request do  
  let(:customer) { FactoryGirl.create(:customer) }
  let(:order_1) { FactoryGirl.create(:order, customer: customer) }
  let(:order_2) { FactoryGirl.create(:order, customer: customer) }

  describe "GET #index" do
    it "responds with all the orders of the customer" do
      get customer_orders_path(id: customer.id)
      expect(json_body.count).to eq(2)
    end
  end

The issue I have with this test is that it fails and that's due to the lazy evaluation of the let statements, which leads to no orders existing in the test database.

One way I could sort out this matter is using instance variables. However, the problem is that I have numerous tests that are using these let statements without any issue and it seems unreasonable that just for a couple of tests, where lazy evaluation is a blocker, I got to refactor all the let statements I have into instance variables. Furthermore, I have numerous let statements (around 20), so the potential idea of keeping both let statements and instance variables in before statements and use them depending on the case would be difficult to manage.

So the only solution I see at the moment is to completely give up on let statements. But I wish there was a better alternative, since let statements are widely considered to be a best practice for their reusability and efficiency.

Is there a better solution that I miss? Is perhaps there a way to trigger the evaluation of the let statements before executing the get request? (even though this seems again to be a dirty workaround..)

1 Answers

If you don't want to refactor anything then an option is to use let!.

Here is a good explanation for let, let!.

let!(:order_1) { FactoryGirl.create(:order, customer: customer) }
let!(:order_2) { FactoryGirl.create(:order, customer: customer) }

Personally I'd do that test like this:

describe Api::OrdersController, type: :controller do  
  let(:customer) { create(:customer) }

  describe "GET #index" do
   it "responds with all the orders of the customer" do
     create_list(:order, 2, customer: customer)
     get customer_orders_path(id: customer.id)
     expect(json_body.count).to eq(2)
   end
  end
end

I usually try to avoid let! because tests need different things to be present in the DB and I don't want to create more than it is required. OR if I can get away with using stubs (build_stubbed, etc), that would be my first choice.

Related