在rspec控制器测试中从具有嵌套关联的工厂正确构造params

时间:2012-05-03 12:40:45

标签: ruby-on-rails rspec

以下是模型关系:

class Tool < ActiveRecord::Base
  ...
  has_many :loss_ratios, :dependent => :destroy, :order => "loss_ratios.starts_at DESC"

  validates_associated :loss_ratios
  accepts_nested_attributes_for :loss_ratios, :allow_destroy => true

  attr_accessible :name, :text, :service_id, :supplier_id, :loss_ratios_attributes
end

class LossRatio < ActiveRecord::Base
  belongs_to :tool

  validates :rate, :starts_at, :tool, :presence => true
  validates_uniqueness_of :starts_at, :scope => :tool_id
  validates_numericality_of :rate
  validates_inclusion_of :rate, :in => (0..1)
  ...
end

我在创建/更新ToolsController操作中管理LossRatio关联。我想通过POST工具的属性集来测试它们(包括几个嵌套的LossRatios,就好像它们是在表单中提交的一样)。我正在使用FactoryGirl,但它似乎没有办法构建类似params的属性哈希(attributes_for忽略了关联,看起来这种行为不会改变)。 有没有办法做到这一点?

(我知道标题很乱,但我无法想到更好更短的内容......)

1 个答案:

答案 0 :(得分:0)

好的,这是我把头发拉了半天后想出来的:

def params_for(factory_name)
  exclude_params = [ "id", "created_at", "updated_at" ]
  f = FactoryGirl.build(factory_name)

  params = f.attributes.except(*exclude_params).dup

  f.reflections.select { |k,v|
    v.macro == :has_many && !v.instance_of?(ActiveRecord::Reflection::ThroughReflection)
  }.each_key do |k|
    assoc_collection = f.send(k)

    unless assoc_collection.empty?
      params["#{k.to_s}_attributes"] = {}

      assoc_collection.each_with_index do |assoc_obj,idx|
        params["#{k.to_s}_attributes"][idx.to_s] = assoc_obj.attributes.except(*exclude_params << "#{f.class.name.underscore}_id")
      end
    end
  end

  params
end

这是一个辅助方法,用于通过控制器的CRUD操作构建params哈希消耗品。我在我的控制器规格中使用它,如:

subject { post :create, :tool => params_for(:tool_with_lr_history) }

it "creates a new tool" do
  expect { subject }.to change(Tool, :count).by(1)
end

从片段中可以看出,该方法仅填充has-many关联的属性(并忽略has-many-through关联)。我想它可能会扩展到任何一种关系,但到目前为止这对我有用(除非有更好的方式做我想做的事)...