【问题标题】:factory girl passing arguments to model definition on build/create工厂女孩在构建/创建时将参数传递给模型定义
【发布时间】:2013-07-31 10:30:33
【问题描述】:

models/message.rb

class Message

  attr_reader :bundle_id, :order_id, :order_number, :event

  def initialize(message)
    hash = message
    @bundle_id = hash[:payload][:bundle_id]
    @order_id  = hash[:payload][:order_id]
    @order_number = hash[:payload][:order_number]
    @event = hash[:concern]
  end
end

spec/models/message_spec.rb

require 'spec_helper'

describe Message do
  it 'should save the payload' do
    payload = {:payload=>{:order_id=>138251, :order_number=>"AW116554416"}, :concern=>"order_create"}
    message = FactoryGirl.build(:message, {:payload=>{:order_id=>138251, :order_number=>"AW116554416"}, :concern=>"order_create"})
    message.event.should == "order_create"
  end
end

错误日志

失败:

1) 消息应该保存有效负载

 Failure/Error: message = FactoryGirl.build(:message, {:payload=>{:order_id=>138251, :order_number=>"AW116554416"}, :concern=>"order_create"})
 ArgumentError:
   wrong number of arguments (0 for 1)
 # ./app/models/message.rb:4:in `initialize'
 # ./spec/models/message_spec.rb:7:in `block (2 levels) in <top (required)>'

【问题讨论】:

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


    【解决方案1】:

    FactoryGirl 要求您首先定义工厂。假设在文件 spec/factories/messages.rb 中:

    FactoryGirl.define do
      factory :message do
        bundle_id 1
        order_id 2
        ...etc...
      end
    end
    

    在此之后,您将能够像这样调用工厂构建创建:

    FactoryGirl.build(:message)  # => bundle_id == 1, order_id == 2
    FactoryGirl.build(:message, order_id: 3) # => bundle_id == 1, order_id == 3
    

    但是,在您的特定情况下存在一个问题。 FactoryGirl 的默认构建器在类似于 ActiveRecord 的界面之上运行。它通过 setter 设置定义的属性,而不是通过传递给模型构造函数的 attrs 哈希:

    m = Message.new
    m.bundle_id = 1
    m.order_id  = 2
    

    所以你必须创建一个自定义构造函数来使用你的模型的接口(它不符合类似 ActiveRecord 的模型)并在你的工厂定义中注册它。详情请见factory girl docs

    让我向您展示一个这样做的例子。对不起,我没有测试它,但它应该给你一个线索:

    FactoryGirl.define do
      factory :message do
        ignore do
          # need to make all attributes transient to avoid FactoryGirl calling setters after object initialization
          bundle_id 1
          order_id 2
        end
    
        initialize_with do
          new(payload: attributes)
        end
      end
    end
    

    【讨论】:

    • 应该注意的是,只有在 FactoryGirl 2.5 支持 initialize_with 之后:robots.thoughtbot.com/factory-girl-2-5-gets-custom-constructors(只有在您因依赖问题而无法使用 rspec 1.x 时才真正重要)
    • 另请注意:DEPRECATION WARNING: `#ignore` is deprecated and will be removed in 5.0. Please use `#transient` instead.
    【解决方案2】:

    这是因为你有一个带有强制参数的构造函数。你有几个选择;

    1) 使参数成为非强制性的(尽管这意味着您正在更改代码以适应您的测试 - 淘气!)

    def initialize(message = nil)
    

    2) 在您的工厂中使用“initialize_with”语法;

    describe Message do
      it 'should save the payload' do
        payload = {:payload=>{:order_id=>138251, :order_number=>"AW116554416"},   :concern=>"order_create"}
        message = FactoryGirl.build(:message, {:payload=>{:order_id=>138251, :order_number=>"AW116554416"}, :concern=>"order_create"})
        message.event.should == "order_create"
      end
    
      initialize_with { new(message) }
    end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-08
      • 2023-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-01
      相关资源
      最近更新 更多