How to create a model with a certain id using rspec and factory girl
Asked Answered
D

5

14

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?

Deflower answered 18/7, 2013 at 15:57 Comment(4)
I'm not sure if this would work, but try id = 7Touraine
Can you add the code for your failing spec, please?Lonilonier
Show the code of your spec if you want useful help. So far everyone is guessing.Disport
Sorry... I added the spec of the failing test.Deflower
B
9

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)

Bloodfin answered 19/7, 2013 at 8:29 Comment(0)
L
13

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)
Lonilonier answered 18/7, 2013 at 16:4 Comment(1)
For those who find this question through Google. This solution works now (August 2015)Bookmark
B
9

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)

Bloodfin answered 19/7, 2013 at 8:29 Comment(0)
T
3

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.

Terylene answered 18/7, 2013 at 17:15 Comment(1)
I agree with you. But the problem is that my HP has a link to this specific user. So, when I run the test the error is that the HP is not rendered since there is no user with id 7 in the test database. This is the only test I have that depends on an id, but I see no other way to avoid this.Deflower
A
1

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) }
Abadan answered 18/7, 2013 at 16:2 Comment(1)
Thanks. The update_attribute did not change the error message... I will try to use the stubs later...Deflower
B
0

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.

Baalbeer answered 8/8, 2016 at 4:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.