2012-04-05 35 views
64

我正在嘗試編寫一個模擬Dropbox REST服務的一些返回值的測試,該服務使用嵌套哈希將數據返回給數組。如何在Factory Girl中定義數組/散列?

我無法搞清楚如何編碼我的工廠,因爲返回結果是一個數組裏面有一個。這裏會有什麼?

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) 
+0

你真的需要爲這個工廠,我可以將屬性?爲什麼不定義一個返回模擬響應的方法? – zetetic 2012-04-06 03:22:26

+0

這就是我最終做的。但我認爲工廠的重點是要隔離這些東西。我仍然很好奇 - 看起來像Hash和Array是類,如果我可以得到正確的語法,這應該工作。 – Doug 2012-04-06 16:41:05

+0

我只用它們來生成ActiveRecord模型實例。 FactoryGirl打算替換燈具。你可以看看RSpec的輔助方法:https://www.relishapp.com/rspec/rspec-core/v/2-9/docs/helper-methods – zetetic 2012-04-06 18:19:25

回答

125

我有興趣做同樣的事情,也測試我的模型,使用來自第三方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將不再嘗試將屬性分配給結果對象。在這種情況下,它似乎也跳過了db存儲。因此,我們只是將已經準備好的屬性散列作爲我們的內容傳回,而不是構造任何複雜的東西。瞧。

似乎有必要爲類指定一些值,儘管它並未實際使用。這是爲了防止factory_girl根據工廠名稱嘗試實例化一個類。我選擇使用描述性類而不是Object,但是取決於你。

,您仍可以當您使用這些哈希工廠之一覆蓋領域:

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

..however,如果您有嵌套內容,並要覆蓋更深的領域,你將需要增加複雜性的初始化塊進行某種深度合併。

就你而言,你使用了一些混合數組和散列數據,並且似乎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) 

您還可以自由省略不必要的值。讓我們想象一下唯一路徑和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) 
+0

這是一個很好的解決方案。它甚至與工廠的繼承一起工作。 – 2014-01-24 18:27:57

4

您可以在最新版本的做到這一點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!
-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 

尋找更好的東西

6

用於當前RSpec的版本(3.0)後續:

只要定義你的工廠像往常一樣,用FactoryGirl.attributes_for接收散列,而不是一個實例化類。

4

得到這個工作對我來說,並根據需要到散

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

這也會讓鑰匙成爲字符串的附加好處。 – crftr 2016-12-22 07:38:12

相關問題