Belongs_to требует ассоциации по умолчанию, и это хорошо, но неудобно для тестирования. Вот как писать отличные тесты независимо
Еще в Rails 5 почтенный атрибут `belongs_to` получил важное обновление: он стал требовать ассоциации по умолчанию.
Как говорит нам Rails, когда мы запускали create!
, он искал нашего пользователя по ID (500) в базе данных. Он обнаружил, что его не существует, и предотвратил создание, вызвав ошибку. Хотя это не такая строгая защита, как ограничение внешнего ключа на уровне базы данных, оно дает нам достаточную защиту от создания потерянных строк в нашей базе данных.
Я большой поклонник этого изменения. Делать что-то неправильно должно быть тяжело. Однако это belongs_to
поведение применяется во всех средах, включая тестовую среду. Для тех из нас, кто создает записи базы данных в тестах, это означает, что мы должны создавать больше данных. Вы знаете, что является главным виновником медленных наборов тестов? Создание данных!
Создание данных причиняет наибольшие неудобства, когда наша база данных находится на другом сервере. Это, конечно, наиболее распространенная конфигурация тестирования непрерывной интеграции для приложений производственного уровня. Каждый раз, когда мы создаем строку, Rails запускает обратный путь к базе данных. Отключения обычно занимают около 20–40 мс. Это не страшно, но, безусловно, имеет смысл, когда один и тот же тест должен создать несколько строк, а у вас есть сотни тестов. Худшее неудобство — это когда у вас есть belongs_to
иерархия:
BillingAddress
принадлежитUser
User
принадлежитUserGroup
UserGroup
принадлежитCompany
Теперь, когда мы начнем писать самый простой тест для контроллера BillingAddress
, он будет выглядеть так:
Святые дымы! Для нашего двухстрочного теста требуется четыре строки настройки и четыре обращения к базе данных! Поскольку круговые обращения к базе данных будут преобладать во времени нашего тестирования при запуске в среде непрерывной интеграции, не будет преувеличением сказать, что наша проверка belongs_to
увеличивает время тестирования в четыре раза.
Читатели, которые работали в крупной компании, использующей Java, знают один выход из этой ситуации: приложив определенные усилия, вы можете отделить запросы к базе данных (уровень постоянства) от уровня создания экземпляров объектов, а затем заглушить вызовы постоянства.
Для толпы Rails это еще проще: вы можете просто залатать метод find
, find_by
или where
, используемый в контроллере, с заглушкой, чтобы вернуть некоторые готовые данные. Если вы используете FactoryBot, эти данные будут предоставлены build
(который использует new
внутри) вместо create
и, таким образом, полностью избегает базы данных. Пропуск create
означает, что Rails не будет запускать проверку существования ассоциации belongs_to
. Это будет выглядеть примерно так:
Вуаля, вообще никакого доступа к базе данных! Пишется быстро и не так уж плохо. Это правильный путь (ПМ).
Однако есть множество ситуаций, которые мы не поднимаем в вежливой беседе, но с которыми сталкиваемся. Например, это тест на ИНТЕГРАЦИЮ, черт возьми! ИЛИ ваш менеджер не верит в заглушки, потому что они порочат естественный порядок вещей (и потому что они пришли из Java). ИЛИ ваш бывший коллега написал все эти ужасные тесты, и вам просто нужно сделать их лучше, а не идеально. Для тебя, мой проницательный программист, у меня есть решение.
ПОДОЖДИТЕ НЕТ, не делайте этого! Хотя этот пример может (абсолютно) основываться на том, что мы действительно делали (до сих пор делаем) в компании, в которой я работал (работаю), это не так. ! (Кроме того, я клянусь, мы реформируемся!)
Это решит вашу проблему; вы сможете create
указать адрес для выставления счетов в своем тесте, но цена будет заключаться в том, что вы потеряете защиту, обеспечиваемую проверкой ассоциации в рабочей среде! На самом деле мы хотим, чтобы проверка не выполнялась в нашей тестовой среде. И было бы здорово, если бы нам не нужно было определять его как параметр для каждого belongs_to
в нашем приложении. К счастью, Rails предоставляет:
С этой единственной строкой в конфигурации вашей тестовой среды каждый belongs_to
будет пропускать проверку только в тестовой среде, что позволит вам написать краткие, яркие тесты, подобные этому:
Хотя я не собираюсь доказывать, что это обязательно должно делать, факт в том, что это очень и очень далеко продвинет вас. Он не требует обслуживания, прост в использовании и требует только одного доступа к базе данных. И он по-прежнему поддерживает проверку belongs_to
, которую вы хотите в средах prod (и даже dev).
Существует контраргумент, что ваша тестовая среда должна вести себя как рабочая среда, насколько это возможно. Это действительно так. Зеркалирование вашей конфигурации разработки и производства будет предупреждать вас (сбоем), когда вы пишете код, который должен создать ассоциацию, но не создает. Однако, посколькувы являетесь ответственным программистом и посколькуважно, чтобы ваш код создавал эту ассоциацию, вам следует проверить, что она делает. Если вам не нравятся риски, используйте заглушки.
Но для остальных из нас, кто наслаждается небольшой дикой поездкой с рельсов, добро пожаловать.
Ресурсы
- Суть отношения Rails, приводящего к ошибке проверки
- Пример теста, создающего данные для набора иерархических отношений принадлежности_к
- Суть того же теста с заглушенным поиском данных
- Суть того же теста с созданной только соответствующей строкой базы данных
- Контрпример, делающий необязательной во всех средах только одно отношение принадлежности_к
- Сущность настройки тестовой среды для обхода проверки author_to