Factory bot Flashcards
Как включить Factory bot в свой Rails проект ?
в Gemfile написать:
group :development, :test do
gem ‘factory_bot_rails’
end
Как отключить использование factory bot для замены fixtures в моем Rails приложении
в application.rb :
config.generators do |g|
g.factory_bot false
end
Для чего нужен Factory bot ?
Для автоматической замены fixtures
- при создании шаблонов тестов генераторами,
- при использовании в тестовых случаях
Какой каталог фабрик по умолчанию ?
test/factories
или
spec/factories
Как изменить каталог фабрик по умолчанию ?
в файле application.rb:
config.generators do |g|
g.factory_bot dir: ‘custom/dir/for/factories’
end
Что будет, если гем factory_bot_rails не включен в группу development ?
Генераторы Rails будут генерировать стандартные файла фикстур .yml
В каком файле описаны определения всех фабрик?
factories.rb в каталоге с тестами по умолчанию.
Какие настройки и куда нужно внести, чтобы использовать factory bot с гемом Rspec?
файл spec/support/factory_bot.rb
RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods
end
Затем в rails_helper.rb загрузить этот файл
require ‘support/factory_bot’
Какие настройки и куда нужно внести, чтобы использовать factory bot с Test::Unit ?
Просто включить модуль в класс теста
class Test::Unit::TestCase
include FactoryBot::Syntax::Methods
end
Какие настройки и куда нужно внести, чтобы использовать factory bot с Minitest ?
Просто включить модуль в класс теста
class Minitest::Unit::TestCase
include FactoryBot::Syntax::Methods
end
Каким образом описывается формирование одной фабрики?
# Тут будет угадана модель User FactoryBot.define do factory :user do first_name "John" last_name "Doe" admin false end end
Как для одного класса модели задать более одной фабрики?
FactoryBot.define do # Тут будет использован явно указанный класс модели User (модель Admin не будет угадываться)
factory :admin, class: User do first_name "Admin" last_name "User" admin true end end
Из каких мест приложения будут загружаться фабрики при выполнении метода
FactoryBot.find_definitions ?
test/factories.rb
spec/factories.rb
test/factories/.rb
spec/factories/.rb
Какие стратегии создания объектов из фабрик поддерживает factory bot?
# Возвращает не сохраненный экземпляр User user = build(:user)
# Возвращает сохраненный экземпляр User user = create(:user)
# Возвращает hash аттрибутов который может быть использован для создания экземпляра класса User attrs = attributes_for(:user)
# Возвращает объект со всеми выделенными (stubbed out) аттрибутами stub = build_stubbed(:user)
Можно ли передавать блоки методам создания объектов ?
При передаче блока любому из этих методов экземпляр, созданный фабрикой, прокидывается внутрь блока
create(:user) do |user|
user.posts.create(attributes_for(:post))
end
Можно ли переопределить значения атрибутов фабрики при создании объектов ?
Да, независимо от стратегии стратегии создания
# Создаем экземпляр User и переопределяем свойство first_name user = build(:user, first_name: "Joe") user.first_name # => "Joe"
Как в factory bot описать динамические атрибуты?
factory :user do # ... activation_code { User.generate_activation_code } date_of_birth { 21.years.ago } end
Для чего в описании фабрик используются псевдонимы Alisces ?
factory :user, aliases: [:author, :commenter] do
first_name “John”
last_name “Doe”
date_of_birth { 18.years.ago }
end
Чтобы упростить их повторное использовние в фабриках связанных моделей
factory :post do author # вместо # ассоциации :author, factory: :user title "How to read a book effectively" body "There are five steps involved." end
factory :comment do commenter # вместо # ассоциации :commenter, factory: :user body "Great article!" end
можно ли в фабриках задавать атрибуты зависящие от значений других атрибутов этой же фабрики?
Да, можно
factory :user do first_name "Joe" last_name "Blow" email { "#{first_name}.#{last_name}@example.com".downcase } end
create(:user, last_name: "Doe").email # => "joe.doe@example.com"
Что такое transient атрибуты и для чего они используются ?
Это способ объявления атрибутов в фабрике для выполнения принципа DRY. Используются для задания статических и динамических атрибутов фабрики
factory :user do transient do rockstar true upcased false end
name { “John Doe#{“ - Rockstar” if rockstar}” }
email { “#{name.downcase}@example.com” }
….
Доступны ли transient атрибуты в методе attributes_for() ?
Нет
Можно ли использовать transient атрибуты в обратных вызовах?
Да, можно.
Для доступа к ним в блок обратного вызова нужно передать второй параметр evaluator
factory :user do transient do rockstar true upcased false end
name { “John Doe#{“ - Rockstar” if rockstar}” }
email { “#{name.downcase}@example.com” }
# Это объявление обратного вызова after(:create) do |user, evaluator| user.name.upcase! if evaluator.upcased end end
create(:user, upcased: true).name #=> "JOHN DOE - ROCKSTAR"
Как объявить атрибут фабрики, если его имя конфликтует с зарезервированными в factory_bot словами ?
Использовать метод add_attribute()
factory :dna do
add_attribute(:sequence) { ‘GATTACA’ }
end
factory :payment do
add_attribute(:method) { ‘paypal’ }
end
Как работает наследование в определении фабрик?
Так
factory :post do
title “A title”
factory :approved_post do
approved true
end
end
approved_post = create(:approved_post)
approved_post.title # => “A title”
approved_post.approved # => true
или указать родителя явно
factory :post do
title “A title”
end
factory :approved_post, parent: :post do
approved true
end
Как использовать связи между моделями при описании фабрик?
Использовать метод association ()
factory :post do
# …
# сразу создается и связанный объект author
association :author, factory: :user, last_name: “Writely”
end
Первый параметр - это имя связи,
второй параметр - это имя фабрики для связанной модели
третьим параметром можно переопределить атрибуты связанной фабрики
Нужно ли использовать метод association(), если имя связанной модели совпадает с именем связи ?
нет, не нужно factory :post do # ... author # имя фабрики совпадает с именем ассоциации для post end
Поведение метода association() меняется в зависимости от стратегии создания объекта.
Как явно передать стратегию создания связанного объекта?
Использовать параметр strategy: :build
factory :post do
# …
# сразу создается и связанный объект author
association :author, factory: :user, strategy: :build
end
# Создает User, а затем создает Post, но ничего не сохраняет post = build(:post) post.new_record? # => true post.author.new_record? # => true
Можно ли использовать параметр strategy: :build в описании неявных ассоциациях?
Нет, это приведет к ошибке.
factory :post do
# …
author strategy: :build # «< это не сработает, потому что author_id будет nil
Для чего нужны последовательности sequence ?
Для генерации однотипных уникальных значений (атрибутов)
# Определяет новую последовательность FactoryBot.define do sequence :email do |n| "person#{n}@example.com" end end
generate :email # => "person1@example.com"
generate :email # => "person2@example.com"
Последовательности также могут иметь псевдонимы (alias).
factory :user do
sequence(:email, 1000, aliases: [:sender, :receiver]) { |n| “person#{n}@example.com” }
end
Как это влияет на счетчик последовательности?
Счетчик общий для всех псевдонимов
# увеличит значение счетчика для :email, также и для :sender и для :receiver generate(:sender)
Должен ли счетчик последовательности всегда быть числом ?
Счетчиком может быть и строка. Главное, чтобы тип данных поддерживал метод #next
factory :user do
sequence(:email, ‘a’, aliases: [:sender, :receiver]) { |n| “person#{n}@example.com” }
end
Можно ли сбросить счетчик последовательности?
Да, с помощью метода FactoryBot.rewind_sequences
sequence(:email) {|n| “person#{n}@example.com” }
generate(:email) # “person1@example.com”
generate(:email) # “person2@example.com”
generate(:email) # “person3@example.com”
FactoryBot.rewind_sequences
generate(:email) # “person1@example.com”
Для чего используются traits в factoru\y bot?
Для создания групп атрибутов, чтобы использовать их в любой фабрике.
factory :user, aliases: [:author]
factory :story do
title “My awesome story”
author
trait :published do
published true
end
trait :unpublished do
published false
end
trait :week_long_publishing do
start_at { 1.week.ago }
end_at { Time.now }
end
trait :month_long_publishing do
start_at { 1.month.ago }
end_at { Time.now }
end
factory :week_long_published_story, traits: [:published, :week_long_publishing]
factory :month_long_published_story, traits: [:published, :month_long_publishing]
factory :week_long_unpublished_story, traits: [:unpublished, :week_long_publishing]
factory :month_long_unpublished_story, traits: [:unpublished, :month_long_publishing]
end