Search code examples
ruby-on-railsrubyrspecfactory-bot

factory_bot build_stubbed strategy


The factory_bot documentation for build strategies says:

factory_bot supports several different build strategies: build, create, attributes_for and build_stubbed

And continues with some examples of usage. However, it doesn't clearly state what the result of each one is. I've been using create and build for a while now. attributes_for seems straightforward from the description and I see some uses for it. However, what is build_stubbed? The description says

Returns an object with all defined attributes stubbed out

What does "stubbed out" mean? How is this different from either create or build?


Solution

  • Let's consider the difference on the example of these factories:

    FactoryBot.define do
      factory :post do
        user
        title { 'Post title' }
        body { 'Post body' } 
      end
    end
    
    FactoryBot.define do
      factory :user do
        first_name { 'John' }
        last_name { 'Doe' }
      end
    end
    

    build

    With build method everything is easy. It returns a Post instance that's not saved

    # initialization
    post = FactoryBot.build(:post)
    
    # call
    
    p post
    p post.user
    
    # output
    #<Post:0x00007fd10f824168> {
       :id => nil,
       :user_id => nil,
       :title => "Post title",
       :body => "Post body",
       :created_at => nil,
       :updated_at => nil
    }
    
    #<User:0x00007f8792ed9290> {
      :id => nil,
      :first_name => "Post title",
      :last_name => "Post body",
      :created_at => nil,
      :updated_at => nil
    }
    
    Post.all # => []
    User.all # => []
    

    create

    With create everything is also quite obvious. It saves and returns a Post instance. But it calls all validations and callbacks and also creates associated instance of User

    # initialization
    post = FactoryBot.create(:post)
    
    # call
    
    p post
    p post.user
    
    # output
    
    #<Post:0x00007fd10f824168> {
       :id => 1,
       :user_id => 1,
       :title => "Post title",
       :body => "Post body",
       :created_at => Sat, 18 Jun 2022 05:32:17.122906000 UTC +00:00,
       :updated_at => Sat, 18 Jun 2022 05:32:17.122906000 UTC +00:00
    }
    #<User:0x00007f8792ed9290> {
      :id => 1,
      :first_name => "John",
      :last_name => "Joe",
      :created_at => Sat, 18 Jun 2022 05:32:17.122906000 UTC +00:00,
      :updated_at => Sat, 18 Jun 2022 05:32:17.122906000 UTC +00:00
    }
    

    Post record and associated user record were created in the database:

    Post.all # => [<Post:0x00007fd10f824168> {...}]
    
    # User also created in the database
    User.all # => [<User:0x00007f91af405b30> {...}]
    

    build_stubbed

    build_stubbed imitates creating. It slubs id, created_at, updated_at and user_id attributes. Also it skips all validations and callbacks.

    Stubs means that FactoryBot just initialize object and assigns values to the id created_at and updated_at attributes so that it just looks like created. For id it assign integer number 1001 (1001 is just default number what FactoryBot uses to assign to id), for created_at and updated_at assigns current datetime. And for every other record created with build_stubbed is will increment number to be assigned to id by 1. First FactoryBot initialize user record and assign 1001 to id attribute but not save it to the database than it initialize post record and assing 1002 to the id attribute and 1001 to user_id attribute to make association, but also doesn't save record to the database. See example below.

    #initialization
    post = FactoryBot.build_stubbed(:post)
    
    # call
    
    p post
    p post.user
    
    # output
    # It looks like persisted instance
    #<Post:0x00007fd10f824168> {
       :id => 1002,
       :user_id => 1001,
       :title => "Post title",
       :body => "Post body",
       :created_at => Sat, 18 Jun 2022 05:32:17.122906000 UTC +00:00,
       :updated_at => Sat, 18 Jun 2022 05:32:17.122906000 UTC +00:00
    }
    
    #<User:0x00007f8792ed9290> {
      :id => 1001,
      :first_name => "John",
      :last_name => "Joe",
      :created_at => Sat, 18 Jun 2022 05:32:17.122906000 UTC +00:00,
      :updated_at => Sat, 18 Jun 2022 05:32:17.122906000 UTC +00:00
    }
    

    Post and user records were not created in the database!!!

    # it is not persisted in the database
    Post.all # => []
    
    # Association was also just stubbed(initialized) and there are no users in the database.
    User.all # => []