FactoryGirl and Rspec
Asked Answered
F

2

8

I've very green to this TDD business, so any help would be fantastic!

So, I've got a factory with the following:

FactoryGirl.define do

  factory :account do
    email "[email protected]"
    url "teststore"
  end  

end

And an Rspec test with:

  it "fails validation without unique email" do
    account1 = FactoryGirl.create(:account)
    account2 = FactoryGirl.create(:account)
    account2.should have(1).error_on(:email)
end

I get a failure with the following message:

  1) Account fails validation without unique email
     Failure/Error: account2 = FactoryGirl.create(:account)
     ActiveRecord::RecordInvalid:
       Validation failed: Email taken, please choose another, Url taken, please choose another
     # ./spec/models/account_spec.rb:11:in `block (2 levels) in <top (required)>'

Is this the correct way to create new factories? Any ideas what I'm doing wrong here (I have no doubt I'm doing something totally incorrect!)

EDIT: I'm thinking of instead of using 'create' on the second account, I may want to use .build and then .save instead?

Flosser answered 8/12, 2011 at 23:40 Comment(0)
O
15

Save yourself the database interactions and use the build method for situations like this.

it "fails validation without unique email" do
  account1 = create(:account)
  account2 = build(:account)
  account2.should_not be_valid
  account2.should have(1).error_on(:email) 
end

You don't need to try and create an account for valid? to return false. You have access to the errors object on the account even when it's just built in memory. This will decrease database interactions and thus making your tests much faster.

Have you considered using sequences in your factories? I don't know how far along you are with your RSpec / FactoryGirl experience, but you will find that things like the following are very useful.

factories.rb

factory :account do
  sequence(:email) { |n| "user#{n}@example.com" }
  url "teststore"
end

Every time you call build or create on the account factory, you will get unique emails.

Remember that you can always specify values for the attributes on the factory using the options hash. So when testing your uniqueness validation on the account, you would do something like this.

it "fails validation without unique email" do
  account1 = create(:account, :email => "[email protected]")
  account2 = build(:account, :email => "[email protected]")
  account2.should_not be_valid
  account2.should have(1).error_on(:email) 
end
Odontology answered 8/12, 2011 at 23:59 Comment(3)
Sorry if this seems dense, but wouldn't that not pass the test because the 'sequence' would create two different emails?Flosser
Awesome, exactly what I was after. Thanks for your help - I'm looking forward to the new and exciting world of testing!Flosser
Glad I could help. When I first got started with testing, it seemed to have slowed my development time down in the beginning stages of the application. Just stay with it and remember how much time it will save you in the future.Odontology
T
0

Try this:

FactoryGirl.create(:account)
lambda {
  FactoryGirl.create(:account)
}.should raise_error(ActiveRecord::RecordInvalid)

This will help - with similar syntax to what you're doing.

However, searching for "rspec validate_uniqueness_of" will find you some more elegant approaches instead of using factory girl like this!

Telencephalon answered 8/12, 2011 at 23:48 Comment(4)
Check out the Shoulda Matchers gem; it will save you some typing: github.com/thoughtbot/shoulda-matchersCerys
Is it fair to say this is a meaningless test to write, because I'm really just testing the validation built into rails? (ie. not my own code)Flosser
This test is not meaningless. You're testing a validation that you set on the Account model (:uniqueness => true). If you (or some other developer) removes this validation, your test will fail.Odontology
This is a very meaningful test. Again a search for testing this behavior will bring shoulda to your attention. :)Telencephalon

© 2022 - 2024 — McMap. All rights reserved.