Архитектурный плагин Pest
Всё начинается с создания файла ArchTest.php
в каталоге tests/Feature
нашего приложения. Отсюда мы можем начать документировать наши архитектурные требования и правила, что позволит протестировать их с помощью отличного API и фантастического опыта разработчиков.
Один тест, который я рекомендую добавить к вашим тестам, гарантирует, что в нашем приложении не останется случайных вызовов отладки.
test('Not debugging statements are left in our code.')
->expect(['dd', 'dump', 'ray'])
->not->toBeUsed();
Прежде чем мы слишком углубимся в доступные правила, которые могли бы использовать, давайте проанализируем структуру этих тестов. Он начинается с вызова теста, где мы определяем имя теста — убедитесь, что это что-то понятное, описывающее, что вы тестируете. Затем мы используем API-интерфейс ожиданий Pest более высокого порядка, чтобы объявить, что мы тестируем. Мы начинаем с ожидания чего-то. В приведённом выше случае мы ожидаем, что ни одно из следующих действий не будет использовано, или могли бы ожидать, что что-то появится.
Далее давайте рассмотрим более конкретные тесты, которые мы можем создать, чтобы убедиться, что наши архитектурные правила соблюдаются. Очевидно, что первое, что нужно сделать, это убедиться, что мы понимаем, каковы наши архитектурные методы. Вы не можете просто применять правила, которым не собираетесь следовать. В этой статье я задокументирую некоторые правила, которые мне нравиться применять в своих проектах. Будучи неуверенным разработчиком, как и я.
test('Our API controllers return responses that we expect')
->expect('Illuminate\Contracts\Support\Http\Responsable')
->toBeUsedIn('App\Http\Controllers\Api');
В этом тесте мы хотим убедиться, что мы используем классы Response в нашем API. Наши контроллеры должны возвращаться классы Responsable, чтобы мы минимизировали дублирование кода и всегда возвращались стандартным образом.
test('We do not directly use Eloquent Models in our APIs.')
->expect('App\Models')
->not->toBeUsedIn('App\Http\Controllers\Api');
В этом тесте мы хотим убедиться, что не используем модели Eloquent напрямую в нашем API. Мы должны использовать классы Action
, Command
и Query
или Service
/Repository
при работе с базой данных. Этому правилу я стараюсь следовать настолько, насколько это возможно, так как оно, уменьшает дублирование кода.
test('We always use Resource classes when responding')
->expect('App\Http\Resources')
->toBeUsedIn('App\Http\Controllers\Api');
В этом тесте мы хотим убедиться, что всегда используем Resource
классы в наших контроллерах. Это позволяет гарантировать, что у нас есть стандартизированный способ возврата данных из API.
Следующие правила будут более конкретными для того, как я пишу код, поэтому они могут не иметь отношения к вам, но они показывают конкретные способы, которыми вы можете протестировать модульные аспекты вашего кода.
Я сильно опираюсь на свой модульный подход к Laravel, используя отдельное пространство имён там, где они мне нужны. Я также сильно опираюсь на Command
для действий записи и Query
для действий чтения. Я могу гарантировать, что они используются там, где это имеет смысл для меня.
test('We use Query classes where we need them in the Catalog domain')
->expect('Domains\Catalog\Queries')
->toBeUsedIn('App\Http\Controllers\Api\V1\Products\Read');
В настоящее время я бы использовал это как временную меру, пока плагин архитектуры не поддерживает подстановочные знаки. Мы хотим убедиться, что используем запросы в нужном месте. Как только мы разрешаем подстановочные знаки в плагине архитектуры, мы можем сделать что-то вроде этого:
test('Query classes are used for read operations')
->expect('Domains\*\Queries')
->toBeUsedIn('App\Http\Controllers\Api\*\*\IndexController')
->toBeUsedIn('App\Http\Controllers\Api\*\*\ShowController')
->not->toBeUsedIn('App\Http\Controllers\Api\*\*\StoreController')
->not->toBeUsedIn('App\Http\Controllers\Api\*\*\UpdateController')
->not->toBeUsedIn('App\Http\Controllers\Api\*\*\DeleteController');
С помощью этого теста мы можем убедиться, что используем запросы во всех наших конечных точках чтения, но не в конечных точках записи, что позволяет лучше контролировать архитектуру кода и подход.
Конечно, сейчас это недоступно, но, возможно, это позволит нам иметь меньше архитектурных правил и такое же покрытие в будущем.