2

我有一个接受 aninvoice及其嵌套的模型items

class Invoice < ActiveRecord::Base

  belongs_to :user
  has_many :items

  attr_accessible :number, :date, :recipient, :project_id, :items_attributes

  accepts_nested_attributes_for :items, :reject_if => :all_blank

end

不过,我发现用 RSpec 和 FactoryGirl 测试它非常困难。这就是我所拥有的:

describe 'POST #create' do

  context "with valid attributes" do

    it "saves the new invoice in the database" do
      expect {
        post :create, invoice: attributes_for(:invoice), items_attributes: [ attributes_for(:item), attributes_for(:item) ]
      }.to change(Invoice, :count).by(1)        
    end

  end

end

这是我在控制器中的创建操作:

def create
  @invoice = current_user.invoices.build(params[:invoice])
  if @invoice.save
    flash[:success] = "Invoice created."
    redirect_to invoices_path
  else
    render :new
  end
end

每当我运行它时,我都会收到一个错误:Can't mass-assign protected attributes: items

有人可以帮我解决这个问题吗?

谢谢...

4

2 回答 2

3

第一:items是嵌套的,所以它们在 params 中的名字是items_attributes. 更改。

第二:嵌套意味着......嵌套!

基本上,替换:

post :create, invoice: attributes_for(:invoice, items: [ build(:item), build(:item) ])

和:

post :create, invoice: { attributes_for(:invoice).merge(items_attributes: [ attributes_for(:item), attributes_for(:item) ]) }

旁注,你在这里做一个真正的集成测试,你可以存根来保持单元测试。

于 2013-03-08T12:52:25.393 回答
1

我遇到了同样的问题,所以我创建了一个补丁,将 FactoryGirl.nested_attributes_for 方法添加到 FactoryGirl:

module FactoryGirl
  def self.nested_attributes_for(factory_sym)
    attrs = FactoryGirl.attributes_for(factory_sym)
    factory = FactoryGirl.factories[factory_sym]
    factory.associations.names.each do |sym|
      attrs["#{sym}_attributes"] = FactoryGirl.attributes_for sym
    end
    return attrs
  end
end

所以现在你可以打电话:

post :create, invoice: FactoryGirl.nested_attributes_for(:invoice) }

你会得到你所知道和喜爱的所有嵌套形式的善良:)

(要应用补丁,您需要将我答案顶部的代码复制到 config/initializers 文件夹中的新文件中)

于 2013-10-03T21:14:23.423 回答