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

Dou*_*oug 78 rspec ruby-on-rails-3 factory-bot

我正在尝试编写一个模拟来自Dropbox的REST服务的一些返回值的测试,该服务通过嵌套哈希为我提供数据.

我无法弄清楚如何编码我的工厂,因为返回结果是一个带有内部的数组.会怎么样?

Factory.define :dropbox_hash do
 ??
end
Run Code Online (Sandbox Code Playgroud)

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"}] 
Run Code Online (Sandbox Code Playgroud)

在我的RSpec中,我想要像这样的工厂电话:

Factory.create(:dropbox_hash)
Run Code Online (Sandbox Code Playgroud)

dou*_*ked 144

我有兴趣做同样的事情,也测试我的模型使用来自第三方API的内容散列操作.我发现通过使用factory_girl的一些内置功能,我能够干净地构建这些类型的数据结构.

这是一个人为的例子:

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

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

    initialize_with { attributes } 
  end
Run Code Online (Sandbox Code Playgroud)

这里的主要技巧是当你声明initialize_with时,factory_girl将不再尝试将属性分配给结果对象.在这种情况下,它似乎也跳过了db存储.因此,我们只是将已准备好的属性哈希作为内容传回,而不是构造任何复杂的东西.瞧.

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

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

chick = FactoryGirl.build(:chicken, name:"Charles")
Run Code Online (Sandbox Code Playgroud)

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

在您的情况下,您正在使用一些混合数组和散列数据,并且似乎应该在数据结构的各个部分之间重用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)
Run Code Online (Sandbox Code Playgroud)

您还可以省略不必要的值.让我们想象只有路径和转速是非常必要的:

  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)
Run Code Online (Sandbox Code Playgroud)


jet*_*roo 9

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

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" }
Run Code Online (Sandbox Code Playgroud)

  • 这还有一个额外的好处,即允许键是字符串. (3认同)

mil*_*ner 7

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

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

  • 该宝石被重命名为FactoryBot。该文档的新位置为https://www.rubydoc.info/github/thoughtbot/factory_bot/FactoryBot/Syntax/Methods#attributes_for-instance_method (2认同)

Joe*_*ris 5

您可以在最新版本的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
Run Code Online (Sandbox Code Playgroud)

在这里浏览怪异的东西:

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