Laravel: Преимущество минимальных фабрик

Источник: «The Benefits of Minimal Factories in Laravel»
Фабрики Laravel значительно экономят время при написании тестов. Раньше я создавал фабрики, чтобы каждое свойство модели находилось в состоянии по умолчанию, но в последнее время я переключился на минимальные фабрики по умолчанию. Я объясню причины этого изменения и какие преимущества я обнаружил.

Давайте рассмотрим конкретный пример: модель UserProfile. У неё есть внешний ключ user_id, два ненулевых свойства, а затем более дюжины обязательных свойств, таких как bio, profile_image и т.д. Когда я говорю о минимальной фабрике по умолчанию, я имею в виду, что она определяет только те свойства, которые необходимы для успешного сохранения в базу данных. Итак, в этой фабрике UserProfile я бы определил только user_id и два моих ненулевых свойства в состоянии фабрики по умолчанию.

Затем я бы создал дополнительные состояния, которые устанавливают необязательные поля. Например, я могу определить состояние hasImage() для установки поля profile_image и состояние hasBio() для установки поля bio. Я так же могу определить состояние fullProfile, которое объединяет все эти индивидуальные хэлперы для заполнения всех возможных полей в этой модели. Затем, если мне нужны дополнительные поля или полностью заполненный профиль, я всё равно могу использовать фабрику, чтобы сэкономить время. (Кстати, это одна из причин, по которой мне очень нравятся изменение в фабриках на основе классов Laravel 8. Так намного проще делится логикой и создавать более выразительную фабрику.)

Итак, почему я сделал это изменение? Какие преимущества я нашёл? Я бы сказал, что самым большим преимуществом на сегодня являются гораздо более читаемые тесты. Возможность увидеть ключевые характеристики модели, которую я создаю, просто взглянув на цепочку фабричных методов в моём тестовом коде, действительно полезна при просмотре набора тестов и попытке быстро понять его.

Говоря о читаемых фабриках в тестах, если модель имеет разные состояния, влияющие на бизнес-логику, я обычно создаю именованное состояние, которое не вносит изменение в состояние по умолчанию, просто для удобства чтения. Например, если у адреса есть свойство type, принимающее значения home, work или other, а состояние по умолчанию устанавливает его как home, я всё равно создам состояние home(), которое ничего не меняет, просто чтобы в тесте было ясно видно, что это домашний адрес. Это не всегда полезно, но когда состояние по умолчанию влияет на бизнес логику, я считаю очень удобным указывать в тесте явно.

Ещё одно преимущество заключается в том, что это может немного ускорить ваши тесты. Зачем в моём примере UserProfile мне просить Faker сгенерировать несколько абзацев текста, а затем заставить базу данных сохранять все эти данные, если это вообще ни как не повлияет на логику моего теста? Да признаю, что это можно было отбросить как микрооптимизацию, но я видел, что она в значительной степени увеличивается по мере роста набора тестов.

Эта экономия производительности особенно актуальна для необязательных отношений. Допустим, ваше приложение позволяет пользователям совершать платежи. Вместо того чтобы ваша UserFactory генерировала несколько случайных платежей я бы делегировал это методу тестирования. Я бы даже не стал добавлять это состояние в UserFactory. Вместо этого, если конкретному тестовому методу требуется, чтобы у пользователя были платежи, он должен зять на себя ответственность за их создание с помощью вызова PaymentFactory.

Дополнительные материалы

Предыдущая Статья

Laravel: Три вещи, которые нужны для TDD

Следующая Статья

TypeScript: Утверждение Типа