How to reload a variable defined by let
Asked Answered
A

3

9

A user has many comments, so I would like to have a factory user with a comment associated to it (user_with_comment):

factory :user, class: User  do |t|
  ...
  factory :user_with_comment do |t|
    after(:create) do |user|
      FactoryGirl.create(:comment, user_id: user.id)
    end
end

It works fine... when I call FactoryGirl.create(:user_with_comment), it creates the user and the related comment in my test db.

However, I'm facing some issues in the controller_spec:

Using let I have to reload the user to see the comment:

let(:user) { FactoryGirl.create(:user_with_comment) }
user.comments.size #=>0
user.reload
user.comments.size #=>1

One solution would be using before(:each), but it will create venda and comment before each test:

before(:each) do
  @user = FactoryGirl.create(:user_with_comment)
end
@user.comments.size #=>1

Or, I can reload the userbefore each test, but it will also hit the database:

let(:user) { FactoryGirl.create(:user_with_comment) }
before(:each) do
  user.reload
end

What is the best approach in this situation?

Aircraft answered 12/2, 2015 at 16:31 Comment(1)
If it needs in one example.. then let is perfect. I think so.Hobbism
C
-5

How about this?

let(:user) { FactoryGirl.create(:user_with_comment).reload }
Casework answered 12/2, 2015 at 18:46 Comment(2)
This won't work because the method is memoized. That .reload will only be run on first use.Buster
That shouldn't matter (and is, in fact, preferable)--they only need to reload the user one time so that the comment created in the factory is present. Though actually, a better approach would be to do this in the factory itself so that every time you use this factory in your test, you don't have to also remember to reload the user.Casework
D
0

I am seeing this while searching for another problem about how to reload a let variable, but here I think the probable solution will be to make the factory use the association itself instead of doing an after_create (not sure if it was available when the question was asked though):

factory :user, class: User  do |t|
  ...
  factory :user_with_comment do
    # might need create(:comment) but in my testing build worked
    comments { [build(:comment)] }
  end
end
let(:user) { FactoryGirl.create(:user_with_comment) }
user.comments.size #=> 1
Dolomite answered 12/5 at 10:54 Comment(0)
S
-2

This has to do with the fact that let is lazy-evaluated and the comment for your user factory is added after the user is created.

Note that let is lazy-evaluated: it is not evaluated until the first time the method it defines is invoked.

See the RSpec documentation on let and let!

Using let means the user created when you first use it in an example and thus the comment is created after that. You can use let! to create the user before each example to avoid the reload. This shouldn't negatively impact your tests if you're using the factory in all/most of the examples in the spec.

Spieler answered 12/2, 2015 at 16:58 Comment(2)
I know the reason of my problem, but your answer does not suggest any solution.... let will not load the comment because the comment is created after the user, so I have to reload it. I am looking for a way to reload it only once, not before each testAircraft
Hmm sorry about that! It seemed as if the issue stemmed from the use of let, hope that info is at least useful to someone. I'm digging a little deeper and trying to recreate. I think something else is wrong here because I created a simple user, comment, and associated factories per your example and the comment is actually there without a reload.Spieler
C
-5

How about this?

let(:user) { FactoryGirl.create(:user_with_comment).reload }
Casework answered 12/2, 2015 at 18:46 Comment(2)
This won't work because the method is memoized. That .reload will only be run on first use.Buster
That shouldn't matter (and is, in fact, preferable)--they only need to reload the user one time so that the comment created in the factory is present. Though actually, a better approach would be to do this in the factory itself so that every time you use this factory in your test, you don't have to also remember to reload the user.Casework

© 2022 - 2024 — McMap. All rights reserved.