【问题标题】:How to test forms with nested attributes using RSpec?如何使用 RSpec 测试具有嵌套属性的表单?
【发布时间】:2023-03-08 23:12:01
【问题描述】:

我有一个Invoice 模型,其中可能包含多个Items

class Invoice < ActiveRecord::Base

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

  belongs_to :user

  has_many :items

  accepts_nested_attributes_for :items, :reject_if => :all_blank, :allow_destroy => true

end

我正在尝试使用 RSpec 对此进行测试:

describe InvoicesController do

  describe 'user access' do

    before :each do
      @user = FactoryGirl.create(:user)
      @invoice = @user.invoices.create(FactoryGirl.attributes_for(:invoice))
      sign_in(@user)
    end

    it "renders the :show view" do
      get :show
      expect(response).to render_template :show
    end

  end

end

很遗憾,此测试(以及所有其他测试)失败,并显示来自 RSpec 的以下错误消息:

Failure/Error: @invoice = @user.invoices.create(FactoryGirl.attributes_for(:invoice))
ActiveModel::MassAssignmentSecurity::Error:
Can't mass-assign protected attributes: items

如何创建包含通过测试的商品的发票?

我正在使用 FactoryGirl 来制作这样的对象:

factory :invoice do
  number { Random.new.rand(0..1000000) }
  recipient { Faker::Name.name }
  date { Time.now.to_date }
  association :user
  items { |i| [i.association(:item)] } 
end

factory :item do
  date { Time.now.to_date }
  description { Faker::Lorem.sentences(1) }
  price 50
  quantity 2
end

【问题讨论】:

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


    【解决方案1】:

    -要在您的示例中使用嵌套属性,您需要传入“item_attributes”,而不是像您当前所做的那样传入“items”。

    我对 FactoryGirl 不熟练,但也许这些方面的东西会起作用? :

    invoice_attributes = FactoryGirl.attributes_for(:invoice)
    invoice_attributes["item_attributes"] = invoice_attributes["items"]
    invoice_attributes["items"] = nil
    @invoice = @user.invoices.create(invoice_attributes)
    

    这应该有望模拟从您的表单传入的参数。

    【讨论】:

    • 谢谢,但这会导致同样的Can't mass-assign protected attributes: items 错误。我什至将您的代码更改为["items_attributes"],这应该是正确的版本,但无济于事:-(
    【解决方案2】:

    编辑:误解了这个问题。道歉。

    代替

    before :each do
      @user = FactoryGirl.create(:user)
      @invoice = @user.invoices.create(FactoryGirl.attributes_for(:invoice))
      sign_in(@user)
    end
    

    只需为使用用户参数传递的发票创建工厂,如下所示:

    before :each do
      @user = FactoryGirl.create(:user)
      FactoryGirl.create :invoice, user: @user
      sign_in(@user)
    end
    

    此外,这是一个次要样式建议,但您可以使用 let 代替实例变量,如下所示:

    let(:user) { FactoryGirl.create :user }
    
    before :each do
      FactoryGirl.create :invoice, user: user
      sign_in(user)
    end
    

    将“用户”传递给发票创建也将创建用户(并且可以简单地调用“用户”)。

    小警告:我已经这样做了大约 6 个月,所以可能会有更博学的人不同意我的风格建议。

    【讨论】:

    • 好的,谢谢,但是你的代码和我的有什么区别?我认为 Rails 的 create 方法也会将 @user.id 保存到新发票中,不是吗?至少在我所有的控制器中都是这样。我会在let 上听取您的建议,但这并不能让我的测试暂时通过:-(
    猜你喜欢
    • 1970-01-01
    • 2011-10-06
    • 1970-01-01
    • 2021-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-18
    相关资源
    最近更新 更多