Jak zdefiniować tablicę / hash w factory_bot?


80

Próbuję napisać test, który symuluje niektóre wartości zwracane z usługi REST Dropbox, która zwraca mi dane w tablicy, z zagnieżdżonym hashem.

Mam problem ze zrozumieniem, jak zakodować moją fabrykę, ponieważ wynikiem zwrotu jest tablica z wartością wewnątrz. Co by tu trafiło?

Factory.define :dropbox_hash do
 ??
end

Dane Dropbox wyglądają następująco:

 ["/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"}] 

I chciałbym mieć taką rozmowę fabryczną w moim RSpec:

Factory.create(:dropbox_hash)

Czy naprawdę potrzebujesz do tego fabryki? Dlaczego po prostu nie zdefiniować metody zwracającej symulowaną odpowiedź?
— zetetic

Właśnie to zrobiłem. Ale myślałem, że celem fabryk było odizolowanie tego materiału. Nadal jestem ciekawy - wygląda na to, że Hash i Array są klasami i to powinno działać, jeśli mogę po prostu uzyskać odpowiednią składnię.
— Doug,

Użyłem ich tylko do generowania instancji modelu ActiveRecord. FactoryGirl ma zastąpić urządzenia. Możesz rzucić
— zetetic

Odpowiedzi:


155

Byłem zainteresowany zrobieniem tego samego, również przetestowaniem mojego modelu, który działa przy użyciu skrótu treści z zewnętrznego interfejsu API. Odkryłem, że korzystając z kilku wbudowanych funkcji factory_girl byłem w stanie w przejrzysty sposób skonstruować tego rodzaju struktury danych.

Oto wymyślony przykład:

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

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

    initialize_with { attributes } 
  end

Główna sztuczka polega na tym, że kiedy deklarujesz initialize_with, factory_girl nie będzie już próbować przypisywać atrybutów do wynikowego obiektu. Wydaje się również, że w tym przypadku pomija sklep db. Więc zamiast konstruować cokolwiek skomplikowanego, po prostu przekazujemy z powrotem już przygotowany hash atrybutu jako naszą zawartość. Voila.

Wydaje się, że konieczne jest określenie jakiejś wartości dla klasy, mimo że w rzeczywistości nie jest ona używana. Ma to na celu zapobieżenie próbie utworzenia instancji klasy na podstawie nazwy fabryki przez factory_girl. Zdecydowałem się użyć klas opisowych zamiast Object, ale to zależy od Ciebie.

Nadal możesz nadpisywać pola, gdy używasz jednej z tych fabryk mieszania:

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

..Jednak jeśli masz zagnieżdżoną zawartość i chcesz zastąpić głębsze pola, będziesz musiał zwiększyć złożoność bloku inicjalizacji, aby wykonać jakieś głębokie scalanie.

W twoim przypadku używasz mieszanej tablicy i danych skrótu i ​​wydaje się, że właściwość Path powinna być ponownie używana między częściami struktury danych. Żaden problem - znasz strukturę zawartości, więc możesz łatwo stworzyć fabrykę, która prawidłowo konstruuje wynikową tablicę. Oto, jak mogę to zrobić:

  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)

Nadal możesz pomijać niepotrzebne wartości. Wyobraźmy sobie, że naprawdę potrzebne są tylko Path i 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)

To świetne rozwiązanie. Działa nawet z dziedziczeniem fabryk.
— David Pelaez

To działa dla mnie do tworzenia / budowania, ale nie działa lint: FacoryGirl.lint
— pixelearth

Dziękuję Ci za to. Aby przepuścić linter Rubocop w favoritesbloku wieloliniowym, użyj do..endzamiast {{..}}.
— scarver2

możesz dodać skip_createdo fabryki, jeśli chcesz zadzwonić do „tworzenia” zamiastbuild
— Sairam

Świetny. Znalazłem, że mogę zainicjować dowolną klasę niestandardową, initialize_withocenia w kontekście klasy, tj. Jeśli mam initialize_with { new(attributes) }, build :my_factoryzwróci wynikMyClass.new(attributes)
— MikDiet

10

to działa dla mnie i mogę przekazywać atrybuty w razie potrzeby do skrótu

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
Ma to również dodatkową zaletę, że klucze mogą być ciągami.
— crftr

minusem jest to, że modyfikujesz pojedynczy obiekt, a nie nowy za każdym razem, gdy jest tworzony. Spójrz na object_id z hashem
— Anthony


6

Możesz to zrobić w najnowszych wersjach factory_girl, ale jest to niewygodne, ponieważ zostało zaprojektowane do tworzenia obiektów, a nie struktur danych. Oto przykład:

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

Omawiając tutaj dziwne rzeczy:

  • Każda fabryka potrzebuje prawidłowej klasy kompilacji, nawet jeśli nie jest używana, więc przeszedłem Objecttutaj, aby zapobiec jej szukaniu DropboxHash.
  • Musisz zignorować wszystkie atrybuty za pomocą ignorebloku, aby nie próbował później przypisywać ich do tablicy, na przykład array.revision = 48.
  • Możesz mu powiedzieć, jak połączyć swój wynik za pomocą initialize_with. Wadą jest to, że musisz ponownie napisać pełną listę atrybutów.
  • Musisz podać pusty to_createblok, aby nie próbował array.save!później dzwonić .

0

Użyłem OpenStruct:

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

Edycja: przepraszam, nie działa jako skrót

W końcu używam wersji statycznej, tylko po to, aby ten hash pochodził z systemu Factory ...

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

szukam czegoś lepszego

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.