Reputation: 817
I use:
gem 'rails', '3.2.11'
gem 'rspec-rails', '2.13.2'
gem 'webrat', '0.7.3'
gem 'factory_girl_rails', '4.1.0'
gem 'spork', '~> 0.9.0.rc'
I want to test my HP where I always have a link to a certain user, so the pages controller for HP contains:
@user = User.find(7)
And the view HP contains:
link_to 'See user', @user
The problem is that all tests fail since test database has no user with id 7. I tried:
FactoryGirl.define do
factory :user do
name "Testuser"
id "7"
end
end
... but this doesn't work. There is always the error:
The spec is this:
describe "GET 'home'" do
before(:each) do
FactoryGirl.create(:user)
end
it "should be successful" do
get 'home'
response.should be_success
end
end
Failure/Error: get 'home' ActiveRecord::RecordNotFound: Couldn't find User with id=7
The HP is working fine in reality, just the test fails. How can I assure this test is not going to fail?
Upvotes: 14
Views: 19053
Reputation: 4638
In your case I am sure you are just checking that the user id is being used instead of a specific ID.
When I write tests I just get the id returned from the database which is far more reliable and closer to what you are doing in reality.
I would write the controller spec something like this.
RSpec.describe MyController, type: :controller do
let(:user) { FactoryGirl.create(:user) }
before do
# login the user here which would resolve which user id is being used
sign_in user #assumes you are using warden test helpers
get :home
end
it "allows the user to the page" do
assert_response :ok
end
end
This should load fine. If you wish to double check that the link is correct you should do that in a feature
spec using something like capybara.
Upvotes: 0
Reputation: 17631
What about mocking / stubbing (much faster spec when not saving objects to database):
let(:user) { FactoryGirl.build(:user) }
let(:id) { '1' }
before do
user.stub(:id).and_return(id) # just to illustrate, not necessary if stubbing the find class method
User.stub(:find).and_return(user)
end
it { expect(user.id).to eq(id) } # just to illustrate
it { expect(response).to be_success }
it { expect(assigns(:user)).to eq(user) }
If you still have issue with the user being only instantiated, you can still use the above technique but replace FactoryGirl.build(:user)
by FactoryGirl.create(:user)
Upvotes: 9
Reputation: 19979
Not an answer but a suspicition... you probably shouldn't write the spec the way you are doing it.
in your spec do something like:
u=FactoryGirl.create(:user)
User.where('id=?',u.id).count.should == 1
Making your tests dependent on specific ids is a recipe for disaster.
Upvotes: 3
Reputation: 10874
Based on the Using Factories section of the documentation, I would set the id
in the spec itself with something like:
@user = FactoryGirl.create(:user, :id => 7)
Upvotes: 13
Reputation: 6382
You can set the ID of a user by doing something like
before
u = FactoryGirl.create(:user)
u.update_attribute(:id, 7)
end
However, this is a little odd. You may even run into a case where there are user's 7.
What you could do though, is use Rspec's stubs to get the proper user. For instance you could do something like
let(:user_7) { FactoryGirl.create(:user) }
before { User.stub(:find).with(7).and_return(user_7) }
Upvotes: 1