Столкнулся на работе с занятной проблемой на Laravel 9.
Однажды, я, прогоняя тесты phpunit, никак не мог добиться их удачного выполнения: они постоянно падали, но написаны гарантированно верно, вчера их запускал. Через какое-то всё прошло. Я не был удивлён: на БД-песочнице проводились работы, вскоре она была перезапущена и я таки прогнал тесты с успехом.
Но осознав произошедшее меня обдало ледяным потом — стоп, а какого хуя? В моих тестах нет подключения к БД!
Начав работу над новым сервисом, я подключил статический анализатор vimeo/psalm с плагином psalm/plugin-laravel. Нацепил на pre-commit хук гита, и, настроив самые жестокие (но подходящие) проверки, больше не вспоминал. Работает складно, не мешает, носом тычет, булшит коммитить не даёт.
Начал гонять какие-то свои тесты. Они были унаследованы от штатного класса Test/Testcase
, в котором подключен трейт CreatesApplication
. В setUp()
методе теста вызывается единственый метод этого трейта $this->createApplication()
. Так на время выполнения тестов создаётся полное окружение бекенда, с контейнерами зависимостей, роутами и прочим. Полезно, однако ошибка происходит именно из-за этого, где-то при инициализации классов.
Расчехлив отладчик, вскоре я внезапно очутился на вызове DB::listen()
. Это метод фасада DB
, который после подключения к БД начинает следить за выполненяемыми запросами (для логирования, например). Этой фигни точно быть не должно.
Тем не менее, компания spatie так не считает. Их пакет spatie/laravel-ray, предназначенный для помощи в отладке проекта, безусловно мешает попыткой подключиться к БД. Разумеется, подключение не удаётся и тесты падают.
Я начал размышлять как можно переопределить это поведение:
- перегрузить/заmockать классы/методы laravel-ray хотя бы в рамках теста — безрезультатно;
- настроить конфиг ray.php, где явно выключил вообще всё — не помогло (
DB::listen()
отрабатывает раньше, чем этот конфиг вообще прочитается); - настроить конфиг с отдельным драйвером для подключения к БД — и сработало.
Сработало в таком виде:
// config/database.php
'connections' => [
...
'testing' => [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => ''
],
...
]
// phpunit.xml
<phpunit ...>
...
<php>
...
<server name="DB_CONNECTION" value="testing"/>
</php>
</phpunit>
Хак классический, в общем, известный, но я подумал — а может его вообще снести нахуй, этот рей? В composer.json
его, ожидаемо, нет — я ведь его не подключал! Значит и удалить штатно не получится.
Попытавшись развязать клубок зависимостей в composer.lock
я понял, что от этого пакета нам просто так не избавиться — это сквозная зависимость вплоть до ядра Laravel (где-то в require
, где-то в require-dev
). Крушить я на тот момент не был готов, погружаться не стал; в конце концов, тесты стали отрабатывать и это меня успокоило.
Через время я наткнулся на похожие ошибки подключения к БД на этапе auto-discovery пакетов после composer require/update/remove/dumpautoload
.
И знаешь что? Это был другой бекенд. Никакой БД в нём никак не участвует, не подключается. Вообще. Меня прошибло дежавю.
Auto-discovery — механизм Laravel (начиная с 5.5), который позволяет обнаруживать composer-пакеты с классами сервис-провайдеров, которые подключаются самостоятельно, без необходимости вручную прописывать их имена в конфигах проекта. И в этот момент, разумеется, отрабатывает знакомый нам spatie/laravel-ray и его DB::listen()
.
Я снова озадачился этой проблемой. Пошёл к людям, мол, чё делать-то? И в конце концов меня ткнули в composer why
.
Помнишь, в начале поста я упоминал psalm/plugin-laravel? Так вот, он тащит за собой orchestra/testbench. А эта сука тянет за собой что? Рей. И обе зависимости в секции require
, а не в require-dev
.
Таким образом получаем ситуацию, когда сам плагин псалма, будучи dev-зависимостью в dev-окружении, по цепочке триггерит сквозные не-dev-зависимости, будто так и надо. В итоге всё либо падает, либо зря подключается к БД. Может быть сейчас это уже кажется логичным, но не перестаёт быть контринтуитивным поведением, причём не только композера, но и самих пакетов.
На время работы над проектом я подключен к корпоративному VPN, за которым находится и БД. В конечном счёте, корректное состояние сети всегда перекрывало некорректное поведение проекта.
Короче, следите за своими зависимостями, пацаны, а то в иной раз легко можете и не отделаться, например.