Reputation: 19929
I am using some of these tools for the first time. I have read through the docs but wanted to ask here exactly what I'm trying to achieve.
I have a set of users that I want to test some actions I can do in a controller spec. When each user is created, there are a set of callbacks that take place to create associated objects.
I'd like to have access to these user instances and the associated objects of that ActiveRecord class. So for example, a user will have a set of lists so I'd like to be able to call user1.lists for example.
Also, I'd like to isolate this setup at the top and use either let's or a before black. It seems that just calling let like this:
# will test that get_count_for_list will return 5
describe ApiController do
# why same name - seems really confusing!
let(:user) { FactoryGirl.create(:user) }
let(:user2) { FactoryGirl.create(:user2) }
doesn't call the associated callbacks. Is this correct? Or is it possibly a timing issue?
I like the syntax of using let and being able to access these objects in my ExampleGroups such as user.id but can't access user.lists. Currently I am doing something like:
# will test that get_count_for_list will return 5
describe ApiController do
# why same name - seems really confusing!
let(:user) { FactoryGirl.create(:user) }
let(:user2) { FactoryGirl.create(:user2) }
let(:user3) { FactoryGirl.create(:user3) }
before do
FactoryGirl.create(:user2)
FactoryGirl.create(:user3)
end
but feel that there has to be a better way. Am I creating these user's twice?
thx
I've isolated the code in question here. The global_id value is created via a callback. It exists correctly in the db and can be accessed via the corresponding find_by_email's but using the user2 var's doesn't provide access.
require 'spec_helper'
# will test that get_count_for_list will return 5
describe ApiController do
# why same name - seems really confusing!
let!(:user) { FactoryGirl.create(:user) }
let!(:user2) { FactoryGirl.create(:user2) }
let!(:user3) { FactoryGirl.create(:user3) }
before do
session[:user_id]=user.id # works
end
describe 'FOLLOW / UNFOLLOW options' do
it 'shall test the ability to follow another user' do
puts "user1: " + user.global_id.to_s # doesn't output anything
u2=User.find_by_email('[email protected]') # corresponds to user2
post :follow, :global_id => user2.global_id # doesn't work
#post :follow, :global_id => u2.global_id #works
u3=User.find_by_email('[email protected]')
puts "user_3" + u3.global_id.to_s # outputs correct value
post :follow, :global_id => user3.global_id #doesn't work
#post :follow, :global_id => u3.global_id # works
post :unfollow, :global_id => user.following.sample(1)
response.code.should eq('200')
end
end
end
Upvotes: 16
Views: 15682
Reputation: 1156
I just solved a similar sounding problem. My user authentication spec was not passing using 'let'.
my broken spec:
describe "signin" do
before { visit signin_path }
describe "with valid information", :js => true do
let(:user) { FactoryGirl.create(:user) }
before do
fill_in "email", with: user.email
fill_in "password", with: user.password
click_button "Log In"
end
it { should have_selector('a', text: "#{user.first} #{user.last}") }
end
end
This was not working. The log-in authentication was failing as if the user record was not actually in the database when my sessions controller tries to authenticate it. I tried replacing let with let! but that did not fix anything. Reading this post, and the link above explaining let and let! I realized that I should not be using let for this spec. Now I have a passing spec:
describe "signin" do
before { visit signin_path }
describe "with valid information", :js => true do
user = FactoryGirl.create(:user)
before do
fill_in "email", with: user.email
fill_in "password", with: user.password
click_button "Log In"
end
it { should have_selector('a', text: "#{user.first} #{user.last}") }
end
end
Upvotes: 3
Reputation: 6720
Check the rspec doc: https://www.relishapp.com/rspec/rspec-core/v/2-11/docs/helper-methods/let-and-let
Note that let is lazy-evaluated: it is not evaluated until the first time the method it defines is invoked. You can use let! to force the method's invocation before each example.
In other words if you use let
along with factory_girl
a record will not be created before let-variable invocation.
The correct code is:
# will test that get_count_for_list will return 5
describe ApiController do
# why same name - seems really confusing!
let!(:user) { FactoryGirl.create(:user) }
let!(:user2) { FactoryGirl.create(:user2) }
Upvotes: 29