Factory Girl failing Rspec validation tests
Asked Answered
C

1

18

I've been trying to get a grasp on writing tests, but having a lot of trouble as the tests never seem to validate the way I want them to. In particular, I've been trying to use Factory Girl as opposed to fixtures - as suggested by a recent Railscasts and other advice I've seen on the net - due to the benefits it professes, and that hasn't worked out.

For example, here is a simple Rspec test for a user model, testing to make sure a username is present...

describe User do
  it "should not be valid without a username" do
    user = FactoryGirl.create(:user, :username => "", :password => "secret")
    user.should_not be_valid
  end
end

And my factories.rb file, if it helps...

FactoryGirl.define do
  factory :user do
    sequence(:username) { |n| "registered-#{n}" }
    password "foobar"
  end
end

When I run 'rake spec,' it tells me...

1) User should not be valid without a username
     Failure/Error: user = FactoryGirl.create(:user, :username => "", :password => "secret")
     ActiveRecord::RecordInvalid:
       Validation failed: Username can't be blank

Ummm...that's the POINT. If I specified that the user should NOT be valid, shouldn't this test actually pass?

If I replace the Factory Girl line and set the user in the test with something like 'user = User.new(:username => "", :password => "secret")', to no surprise the test passes fine. So why is Factory Girl not working right?

Cammack answered 21/11, 2011 at 17:59 Comment(0)
O
22

You should use build like in the following:

user = Factory.build(:user, :username=>"foo")

Because using the method you're using will try to create a record. See docs for further information.

Outspeak answered 21/11, 2011 at 18:3 Comment(8)
To amplify this point, if you use create, then it tries to create the invalid record, throws the appropriate exception, and never gets to the next line where your assertion is. build will build the record in memory but not save it, so you won't get the RecordInvalid exception, but user.valid? will still return false.Penelopepeneplain
Why doesn't FactoryGirl just have create and create! just like rails? This can be super irritating when you want to test validation or are trying to debug. Like in rails, sometimes you want one, sometimes the other. There are crazy workarounds out there for creating a new creation strategy and specifying it when you define a factory but all this could be avoided if create behaved as one would expect it to or at least give you some options.Woodsia
@Woodsia I've never figured out what Rails create is useful for -- generally, I want Rails to throw an exception if I try to save an invalid record (so I use create! almost all the time). In fact, I almost always have DB constraints in place so that invalid records can't be saved. And that goes double in tests. If you're building an invalid record, just use build rather than create.Penelopepeneplain
I had similar issue with testing failed state machine transition (triggered by state_event). build + before{ subject.save } worked perfectly.Este
@MarnenLaibow-Koser - create is useful when you want the request to be idempotent. For example, suppose you want to track if a user has viewed a certain page. You could do PageView.create(user: user).Affricate
According to the docs: To create an instance, it calls new...and then calls save!Affricate
@Woodsia - I think the rationale is to promote the error (fail quickly). If Factory Girl used save and silently failed, then we would be surprised at the end of a test when it failed because the validations failed. It is better to know before the test starts. Also, it doesn't make sense to create an invalid record. So, if you know the record will be invalid, then it should be built instead. FWIW, I found that one of the most difficult parts of testing was using FactoryGirl and learning how it works. But after learning the tricks there are few surprises.Affricate
@BSeven That shouldn't throw an exception, though, so I don't think create! would affect idempotency.Penelopepeneplain

© 2022 - 2024 — McMap. All rights reserved.