Як визначити масив / хеш у factory_bot?


80

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

У мене виникають проблеми з тим, як зрозуміти, як кодувати мою Фабрику, оскільки результат повернення - це масив із внутрішнім символом has. Що тут піде?

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)

Вам справді для цього потрібна фабрика? Чому б просто не визначити метод, який повертає змодельовану відповідь?
zetetic

Це я в підсумку зробив. Але я думав, що суть фабрик полягає у тому, щоб ізолювати ці речі. Мені все ще цікаво - здається, Hash і Array - це класи, і це має спрацювати, якщо я можу просто отримати правильний синтаксис.
Даг,

Я використовував їх лише для створення екземплярів моделі ActiveRecord. FactoryGirl призначена для заміни світильників. Ви можете поглянути на допоміжні методи RSpec: relishapp.com/rspec/rspec-core/v/2-9/docs/helper-methods
zetetic

Відповіді:


155

Мені було цікаво зробити те саме, також протестувати мою модель, яка працює, використовуючи хеш контенту від сторонніх 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")

.. Однак, якщо у вас є вкладений вміст і ви хочете замінити більш глибокі поля, вам потрібно буде збільшити складність блоку ініціалізації, щоб зробити якесь глибоке злиття.

У вашому випадку ви використовуєте змішані масиви та хеш-дані, і, схоже, властивість 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)

Ви все ще можете вільно опускати непотрібні значення. Давайте уявимо, що дійсно необхідні лише 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)

Це чудове рішення. Це працює навіть із спадщиною фабрик.
Девід Пелаес,

Це працює для мене для створення / побудови, але це не дає змоги: FacoryGirl.lint
pixelearth

Дякую за це. Для передачі лінтера Rubocop у favoritesбагаторядковому блоці використовуйте do..endзамість {{..}}.
scarver2

Ви можете додати skip_createна завод, якщо хочете зателефонувати "створити" замістьbuild
Сайрам

Чудово. Я виявив, що можу ініціалізувати будь-який власний клас, initialize_withоцінювати в контексті класу, тобто якщо маю initialize_with { new(attributes) }, build :my_factoryповерне результатMyClass.new(attributes)
MikDiet

10

це працює для мене, і я можу передавати атрибути за необхідності в хеш

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
Це також має додаткову перевагу, дозволяючи клавішам бути рядками.
crftr

недоліком тут є те, що ви змінюєте один об’єкт, а не новий, щоразу при його створенні. Погляньте на object_id hash's
Ентоні

8

Продовження для поточної версії RSpec (3.0):

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


2
Самоцвіт було перейменовано на FactoryBot. Новим місцем для цієї документації є rubydoc.info/github/thoughtbot/factory_bot/FactoryBot/Syntax/…
scarver2

6

Ви можете зробити це в останніх версіях 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!потім зателефонувати .

0

Я використовував 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

шукає чогось кращого

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.