Speeding up associations in model specs with FactoryGirl - create vs build vs build_stubbed

Viewed 6735

Say I have models User and Post, a user has_many posts and a post belongs_to a user.

When I write a spec for Post, my first instinct is to write something like this:

before do
  @user = FactoryGirl.create :user
  @post = @user.posts.new(title: "Foo", content: "bar)
end

... tests for @post go here ...

But this is going to create a new User - hitting the database - for every single test, which is going to slow things down. Is there a better way to do this that will speed my tests up and avoid hitting the DB so often?

As I understand it, I can't use FactoryGirl.build :user because, even though it won't hit the DB, the associations won't work properly because @user won't have an ID and so @post.user won't work (it returns nil.)

I could use FactoryGirl.build_stubbed :user which created a "fake persisted" @user which does have an ID, but @post.user still returns nil. Does build_stubbed have any practical advantage over build when I'm testing things related to associations?

I suppose I could use build_stubbed stub @post.user so it returns @user... is there any reason this might be a bad idea?

Or should I just use create and accept the speed hit?

The only other alternative I can think of would be to set up @user in a before(:all) block which seems like a bad idea.

What's the best way to write these kind of tests in a clean, concise way that avoids making too many DB queries?

5 Answers

From the document of factory girl, you can identify strategy build for user in association in post factory like this:

factory :post do
  association :user, factory: :user, strategy: :build
end

So that you can build a post without save user

post = build(:post)
post.new_record?        # => true
post.author.new_record? # => true

A quick explanation of differences: FactoryGirl.create will create new object and associations (if the factory has any) for it. They will all be persisted in db. Also, it will trigger both model and database validations. Callbacks after(:build) and after(:create) will be called after the factory is saved. Also before(:create) will be called before the factory is saved.

FactoryGirl.build won't save an object, but will still make requests to a database if the factory has associations. It will trigger validations only for associated objects. Callback after(:build) will be called after the factory is built.

FactoryGirl.build_stubbed does not call database at all. It creates and assigns attributes to an object to make it behave like an instantiated object. It provides a fake id and created_at. Associations, if any, will be created via build_stubbed too. It will not trigger any validations.

Read full explanation here

Related