如何在factory_bot中定义数组/哈希?


80

我正在尝试编写一个测试,该测试模拟Dropbox的REST服务的一些返回值,该返回值将我的数据以嵌套哈希的形式返回给Array。

由于返回结果是一个内部有has的数组,因此我很难弄清楚如何编写Factory代码。会怎么办?

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)

您真的需要工厂吗?为什么不只定义返回模拟响应的方法?
zetetic 2012年

那就是我最终要做的。但是我认为工厂的重点是隔离这些东西。我仍然很好奇-好像Hash和Array是类,如果我能得到正确的语法,这应该可以工作。
Doug 2012年

我仅将它们用于生成ActiveRecord模型实例。FactoryGirl旨在替换固定装置。您可以看一下RSpec的帮助器方法:relishapp.com/rspec/rspec-core/v/2-9/docs/helper-methods
zetetic 2012年

Answers:


152

我对做同样的事情感兴趣,还想测试我的模型,该模型使用第3方API的内容哈希值进行操作。我发现通过使用factory_girl的一些内置功能,我可以干净地构造这些数据结构。

这是一个人为的例子:

  factory :chicken, class:Hash do
    name "Sebastian"
    colors ["white", "orange"]

    favorites {{
      "PETC" => "http://www.petc.org"
    }}

    initialize_with { attributes } 
  end

这里的主要技巧是,当您声明initialize_with时,factory_girl将不再尝试将属性分配给结果对象。在这种情况下,似乎也跳过了数据库存储。因此,我们无需传递任何复杂的信息,而只是将已经准备好的属性哈希作为内容传递回来。瞧

尽管实际上并未使用该类,但确实有必要为其指定一些值。这是为了防止factory_girl尝试根据工厂名称实例化类。我选择使用描述性类而不是对象,但这取决于您。

使用以下哈希工厂之一时,您仍然可以覆盖字段:

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

..但是,如果您嵌套了内容并想覆盖更深的字段,则需要增加初始化块的复杂度才能进行某种深度合并。

在您的情况下,您正在使用一些混合的数组和哈希数据,并且似乎应该在数据结构的各部分之间重用Path属性。没问题-您知道内容的结构,因此您可以轻松创建一个工厂,以正确地构造结果数组。这是我可能的方法:

  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)

这是一个很好的解决方案。它甚至适用于工厂的继承。
David Pelaez,2014年

这对我来说可以创建/构建,但是失败了:FacoryGirl.lint
pixelearth,

这次真是万分感谢。要在favorites多行块中传递Rubocop短绒,请使用do..end代替{{..}}
scarver2

您可以添加skip_create到工厂,如果您想调用“创建”而不是build
Sairam

10

为我工作了,我可以根据需要将属性传递给哈希

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" }

3
这还将具有允许键为字符串的附加好处。
crftr

缺点是您要修改单个对象,而不是每次创建一个新对象。看看哈希的object_id-
安东尼,


6

您可以在最新版本的factory_girl中执行此操作,但是这样做很尴尬,因为它是用来构建对象而不是数据结构的。这是一个例子:

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

在这里浏览怪异的东西:

  • 即使没有使用,每个工厂都需要一个有效的构建类,因此我Object在这里通过以防止其寻找DropboxHash
  • 您需要使用一个ignore块来忽略所有属性,以免以后再尝试将它们分配给数组array.revision = 48
  • 您可以使用告诉它如何将结果汇总在一起initialize_with。缺点是您需要再次写出完整的属性列表。
  • 您需要提供一个空to_create块,以免array.save!以后再调用。

0

我使用了OpenStruct:

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

编辑:对不起,不能作为哈希

我最终使用了一个静态版本,只是为了使哈希值来自Factory系统。

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

寻找更好的东西

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.