如何在Factory Girl中定义数组/散列?

我正在尝试编写一个testing,模拟Dropbox的REST服务中的一些返回值,这个服务使用嵌套哈希将数据返回给数组。

我无法弄清楚如何编写我的工厂,因为返回结果是一个有一个内部的数组。 这里会有什么?

Factory.define :dropbox_hash do ?? end 

Dropbox的数据如下所示:

  ["/home", {"revision"=>48, "rev"=>"30054214dc", "thumb_exists"=>false, "bytes"=>0, "modified"=>"Thu, 29 Dec 2011 01:53:26 +0000", "path"=>"/Home", "is_dir"=>true, "icon"=>"folder_app", "root"=>"app_folder", "size"=>"0 bytes"}] 

而且我想在我的RSpec中使用这样的工厂调用:

 Factory.create(:dropbox_hash) 

我有兴趣做同样的事情,也testing我的模型,使用来自第三方API的内容散列运行。 我发现通过使用factory_girl的一些内置function,我能够干净地构build这些types的数据结构。

这是一个人为的例子:

  factory :chicken, class:Hash do name "Sebastian" colors ["white", "orange"] favorites {{ "PETC" => "http://www.petc.org" }} initialize_with { attributes } end 

这里的主要技巧是,当你声明initialize_with时,factory_girl将不再试图将属性赋值给结果对象。 在这种情况下,它似乎也跳过了db存储。 所以,我们不是构造任何复杂的东西,而是将已经准备好的属性散列作为我们的内容。 瞧。

似乎有必要为类指定一些值,尽pipe它实际上并没有被使用。 这是为了防止factory_girl根据工厂名称尝试实例化一个类。 我select了使用描述性的类而不是Object,但是取决于你。

当您使用其中一个哈希工厂时,您仍然可以覆盖字段:

 chick = FactoryGirl.build(:chicken, name:"Charles") 

..但是,如果你有嵌套的内容,并想覆盖更深的领域,你将需要增加初始化块的复杂性来做某种深度合并。

在你的情况下,你正在使用一些混合的数组和散列数据,看起来Path属性应该在数据结构的各个部分之间重用。 没问题 – 你知道内容的结构,所以你可以很容易地创build一个工厂,正确地构造出结果数组。 以下是我可能会这样做的:

  factory :dropbox_hash, class:Array do path "/home" revision 48 rev "30054214dc" thumb_exists false bytes 0 modified { 3.days.ago } is_dir true icon "folder_app" root "app_folder" size "0 bytes" initialize_with { [ attributes[:path], attributes ] } end FactoryGirl.build(:dropbox_hash, path:"/Chickens", is_dir:false) 

您也可以自由省略不必要的值。 让我们想象一下,只有Path和rev是非常必要的:

  factory :dropbox_hash, class:Array do path "/home" rev "30054214dc" initialize_with { [ attributes[:path], attributes ] } end FactoryGirl.build(:dropbox_hash, path:"/Chickens", revision:99, modified:Time.now) 

当前RSpec版本(3.0)的后续版本:

像往常一样定义你的工厂,并使用FactoryGirl.attributes_for接收一个哈希,而不是一个实例化的类。

您可以在factory_girl的最新版本中执行此操作,但由于它旨在构build对象而不是数据结构,因此很难实现。 这是一个例子:

 FactoryGirl.define do factory :dropbox_hash, :class => 'Object' do ignore do url { "/home" } revision { 48 } rev { "30054214dc" } # more attributes end initialize_with { [url, { "revision" => revision, "rev" => rev, ... }] } to_create {} end end 

在这里回顾一下奇怪的东西:

  • 即使没有使用,每个工厂都需要一个有效的构build类,所以我在这里传递了Object来防止它查找DropboxHash
  • 你需要忽略所有使用ignore块的属性,这样它不会尝试将它们分配给数组,比如array.revision = 48
  • 你可以告诉它如何使用initialize_with把你的结果放在一起。 这里的缺点是你需要再次写出完整的属性列表。
  • 您需要提供一个空的to_create块,以便它不会尝试调用array.save! 之后。

得到这个为我工作,我可以根据需要传递属性到哈希

 factory :some_name, class:Hash do defaults = { foo: "bar", baz: "baff" } initialize_with{ defaults.merge(attributes) } end > build :some_name, foo: "foobar" #will give you > { foo: "foobar", baz: "baff" } 

我使用OpenStruct:

 factory :factory_hash, class:OpenStruct do foo "bar" si "flar" end 

编辑:对不起,不作为散列工作

我终于使用了一个静态版本,只是为了保持来自工厂系统的散列…

 factory :factory_hash, class:Hash do initialize_with { { foo "bar" si "flar" } } end 

寻找更好的东西