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)
Doug
źródło
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)
podwójnie oznaczony
źródło
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" }
jethroo
źródło
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ć .
Joe Ferris
źródło
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

jobwat
źródło