【问题标题】:FactoryGirl How create a instance which is created by association on callbackFactoryGirl 如何创建一个通过回调关联创建的实例
【发布时间】:2016-05-31 06:35:54
【问题描述】:

考虑两个模型:

class User < ActiveRecord::Base

  has_one :book

  after_create :create_book
end

class Book < ActiveRecord::Base
  belongs_to :user

  validate_uniqueness :user_id
end

每个用户可以拥有也只能拥有一本书。然后我在我的规范中定义了两个工厂:

factory :user do
end

factory book do
  user
end

那么问题来了,当我为Book 编写测试时,我想在使用FactoryGirl.create(:book) 时为Book 创建一条记录book1(我们称之为)。它将创建Book 的实例,然后尝试创建定义user 的关联。创建用户后,after_create为触发器,book2user创建。然后它尝试将book1user 绑定,并被唯一性关联阻止。

现在我正在使用book = FactoryGirl.create(:user).book。这是最好/正确的方法吗?我认为它的注释很直观,因为我正在测试Book,我认为最好有book = FactoryGirl.create(:book)

非常感谢。

【问题讨论】:

    标签: ruby-on-rails testing rspec factory-bot rspec-rails


    【解决方案1】:

    我认为我们可以为此使用trait。示例如下:

    工厂

    factory :user do
      # Your config here
    
      # Use trait
      trait :without_book do
        after(:build) do |user|
          allow(user).to receive(:create_book).and_return true
        end
      end
    
      trait :with_book do
        allow(user).to receive(:create_book).and_call_original
      end
    
      transient do
         # Use this by default but don't use this line also works
         # because we create book in the normal behavior
         with_book
      end
    end
    

    规格

    context 'test user without book' do
      let(:user) { FactoryGirl.create(:user, :without_book)
    
      it 'blah blah' do
      end
    end
    
    context 'test user with book' do
      let(:user) { FactoryGirl.create(:user, :with_book)
      # Or simply use this, because :with_book is default
      # let(:user) { FactoryGirl.create(:user)
    
      it 'blah blah' do
      end
    end
    

    顺便说一句,正如你所见,我在allow(user).to receive(:create_book).and_return true 使用了一个存根方法,基本上,这个实用方法来自rspec-mock,我们需要这个配置才能使其在工厂中可用:

    spec/rails_helper.rb

    FactoryGirl::SyntaxRunner.class_eval do
      include RSpec::Mocks::ExampleMethods
    end
    

    理想情况下,您可以使用trait 为用户处理createnot create 一本书,这样更容易模拟场景!

    【讨论】:

    • 好主意。但是我得到了undefined method and_return' for #<:declaration::static:0x007fd0fe140c18> (NoMethodError)` 尝试你的样本,我不知道为什么。但我解决了after(:build) { |user| user.class.skip_callback(:create, :after, :create_book_for_user ) }
    • 你是否在 rails_helper.rb 中包含include RSpec::Mocks::ExampleMethods
    • 是的,很抱歉我一开始错过了。我只是在尝试,include RSpec::Mocks::ExmpleMethods 在我的rails_helper.rb 中。我仍然为#<:syntaxrunner:0x007fa33a963e00>` 得到NoMethodError: undefined method allow'
    • 嘿,应该是RSpec::Mocks::ExampleMethods,顺便说一句,您使用的是哪个 rspec-mock 版本?
    • 是的,我在我的代码中拼写正确。我正在使用rspec-mocks (~&gt; 3.4.0)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多