Как определить массив/хэш в Factory Девушка?

Я пытаюсь написать тест, который имитирует некоторые возвращаемые значения из службы Dropbox REST, которая возвращает мне данные в массиве с вложенным хешем.

Мне трудно понять, как закодировать мой Factory, так как результатом возврата является массив с внутренним. Что будет здесь?

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

И я бы хотел, чтобы вызов Factory вроде этого в моем RSpec:

Factory.create(:dropbox_hash)

Ответ 1

Мне было интересно сделать то же самое, а также проверить мою модель, которая работает с использованием хеша контента из стороннего 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 создать экземпляр класса на основе имени factory. Я решил использовать дескриптивные классы, а не Object, но это зависит от вас.

Вы по-прежнему можете переопределять поля при использовании одной из этих фабрик хеширования:

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

.. однако, если у вас есть вложенный контент и вы хотите переопределить более глубокие поля, вам нужно будет увеличить сложность блока инициализации, чтобы выполнить какое-то глубокое слияние.

В вашем случае вы используете смешанные массивы и хэш-данные, и кажется, что свойство Path должно быть повторно использовано между частями структуры данных. Нет проблем - вы знаете структуру контента, поэтому можете легко создать factory, который правильно построит результирующий массив. Вот как я могу это сделать:

  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)

Ответ 2

Продолжение для текущей версии RSpec (3.0):

Просто определите свою фабрику как обычно и используйте FactoryBot.attributes_for для получения хеша вместо экземпляра класса.

Ответ 3

получил эту работу для меня, и я могу передавать атрибуты по мере необходимости в хэш

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

Ответ 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

Перейдя сюда странно:

  • Каждому factory нужен допустимый класс сборки, даже если он не используется, поэтому я передал Object здесь, чтобы он не искал DropboxHash.
  • Вам нужно игнорировать все атрибуты с помощью блока ignore, чтобы потом не пытаться назначить их массиву, например array.revision = 48.
  • Вы можете сказать, как скомпоновать ваш результат, используя initialize_with. Недостатком здесь является то, что вам нужно снова записать полный список атрибутов.
  • Вам нужно предоставить пустой блок to_create, чтобы он не пытался вызывать array.save! после этого.

Ответ 5

Я использовал OpenStruct:

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

Изменить: извините, не работает как хэш

Наконец, я использую статическую версию, чтобы сохранить этот хеш из системы Factory...

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

ищет что-то лучшее