Вопрос по ruby-on-rails-3, rspec, factory-bot – Как определить массив / хэш в Factory Girl?

72

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

У меня возникают проблемы с выяснением того, как закодировать мою фабрику, поскольку возвращаемый результат - массив с внутренним значением. Что пойдет сюда?

<code>Factory.define :dropbox_hash do
 ??
end
</code>

Данные Dropbox выглядят так:

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

И мне нравится заводской вызов в RSpec:

<code>Factory.create(:dropbox_hash)
</code>
Я использовал их только для генерации экземпляров модели ActiveRecord. FactoryGirl предназначена для замены светильников. Вы можете взглянуть на вспомогательные методы RSpec:relishapp.com/rspec/rspec-core/v/2-9/docs/helper-methods zetetic
Это то, что я в итоге сделал. Но я думал, что целью Фабрики было изолировать этот материал. Мне все еще любопытно - кажется, что Hash и Array являются классами, и это должно работать, если я могу просто получить правильный синтаксис. Doug
Вам действительно нужен завод для этого? Почему бы просто не определить метод, который возвращает смоделированный ответ? zetetic

Ваш Ответ

5   ответов
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

Рассмотрим странные вещи здесь:

  • Every factory needs a valid build class even if it's not used, so I passed Object here to prevent it from looking for DropboxHash.
  • You need to ignore all the attributes using an ignore block so that it doesn't try to assign them to the array afterwards, like array.revision = 48.
  • You can tell it how to put your result together using initialize_with. The downside here is that you need to write out the full attribute list again.
  • You need to provide an empty to_create block so that it doesn't try to call array.save! afterwards.
6

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

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" }
Это также даст дополнительное преимущество, позволяя ключам быть строками.
6

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

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

Драгоценный камень был переименован в FactoryBot. Новое место для этой документацииrubydoc.info/github/thoughtbot/factory_bot/FactoryBot/Syntax/…
-1

Я использовал 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

в поисках чего-то лучшего

138

Мне было интересно сделать то же самое, а также протестировать мою модель, которая работает с использованием хэша контента от стороннего 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 больше не будет пытаться присвоить атрибуты результирующему объекту. Похоже, что в этом случае пропустить магазин БД. Таким образом, вместо создания чего-либо сложного, мы просто передаем обратно уже подготовленный хэш атрибута в качестве нашего содержимого. Вуаля.

Кажется необходимым указать какое-то значение для класса, несмотря на то, что оно на самом деле не используется. Это необходимо для того, чтобы 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)
Это отличное решение. Это даже работает с фабриками & apos; наследование.
Это работает для меня, чтобы создать / построить, но это терпит неудачу lint: FacoryGirl.lint
Спасибо тебе за это. Чтобы передать Rubocop Linter вfavorites использование многострочного блокаdo..end вместо{{..}}.

Похожие вопросы