80

我正在尝试编写一个测试来模拟来自 Dropbox 的 REST 服务的一些返回值,该服务给我返回数组中的数据,并带有嵌套的哈希。

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

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)
4

5 回答 5

161

我有兴趣做同样的事情,也想测试我的一个模型,该模型使用来自 3rd-party 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 尝试根据工厂名称实例化一个类。我选择使用描述性类而不是 Object,但这取决于你。

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

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)
于 2012-08-19T13:45:21.823 回答
11

让这对我有用,我可以根据需要将属性传递到哈希中

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" }
于 2014-10-29T12:05:07.423 回答
8

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

只需像往常一样定义您的工厂并使用它FactoryBot.attributes_for来接收哈希而不是实例化的类。

于 2014-07-17T12:48:01.497 回答
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!之后尝试调用。
于 2012-05-03T12:28:14.620 回答
1

我使用了 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

寻找更好的东西

于 2012-11-07T05:44:20.963 回答