dk-dev/testrine
最新稳定版本:0.1.18
Composer 安装命令:
composer require dk-dev/testrine
包简介
README 文档
README
Пакет покрывает приложение тестами, затем на основе тестов генерирует документацию к API
установка
composer required dk-dev/testrine
публикация данных
php artisan vendor:publish --provider="DkDev\Testrine\TestrineServiceProvider"
Общая схема работы.
Определяема конфигурацию -> создаем базовые классы для каждой группы -> покрываем тестами -> прогоняем тесты -> коллекторы собирают данные -> процессоры формируют документацию -> сохраняем документацию.
Далее все будет описано детальней.
настраиваем конфигурацию.
у нас могут быть различные группы (например web, api, admin), в каждой группе могут быть свои пользователи, свои коды ответа и т.д.
у нас уже определенна дефолтная группа. Ее настройки применяются ко всем другим группам, но в формировании тестов эта группа не участвует.
в исходной документации мы определяем группу api, все ее настройки получаем из группы default, но мы можем их переопределить.
теперь детальней разберем каждый блок в конфигурации групп.
'groups' => [ 'default' => [ 'users' => [ 'guest', 'user', ], 'document' => true, 'contracts' => [ CodeContract::class => CodeContractResolver::class, DocIgnoreContract::class => DocIgnoreResolver::class, FakeStorageContract::class => FakeStorageResolver::class, InvalidateCodeContract::class => InvalidateContractResolver::class, InvalidateContract::class => InvalidateContractResolver::class, InvalidParametersCodeContract::class => InvalidParametersCodeResolver::class, InvalidParametersContract::class => InvalidParametersContractResolver::class, JobContract::class => JobResolver::class, MockContract::class => MockResolver::class, NotificationContract::class => NotificationResolver::class, ParametersContract::class => ParametersContractResolver::class, SeedContract::class => SeedResolver::class, ValidateContract::class => ValidateContractResolver::class, ResponseContract::class => ResponseContractResolver::class, ], 'code' => [ 'valid_data' => ValidDataCodeResolver::class, 'invalid_data' => InvalidDataCodeResolver::class, 'invalid_route_params' => InvalidRouteParamsResolver::class, ], 'auth_middleware' => 'auth:sanctum', 'auth' => [ 'guest' => WithoutAuthStrategy::class, 'user' => SanctumAuthStrategy::class, ], ], 'api' => [], ],
в users мы определяем, какие пользователи будут использованы в рамках группы. Например, в api может быть только guest и user, к примеру в группе web могут быть performer, customer и guest. И могут быть другие группы со своими пользователями.
'users' => [ 'guest', 'user', ],
Теперь важный момент! Для каждого пользователя нужно будет реализовать методы в классе TestCase или в базовых классах, которые мы создадим далее
Вот пример реализации для изначального конфига
<?php namespace Tests; use App\Models\User; use Illuminate\Foundation\Testing\TestCase as BaseTestCase; abstract class TestCase extends BaseTestCase { use CreatesApplication; public function getUser(): User { return User::query()->first(); } public function getGuest() { return null; } }
Далее переходим к параметру document, этот параметр нужен если нам нужно включить/выключить сбор данных по группе.
'document' => true,
Далее важный момент. Мы определяем какие контракты будут использоваться в группе и какие ресолверы будут определять нужен ли этот контракт для каждого текущего маршрута. Этот блок используется, когда вызывается консольная команда, которая покрывает тестами маршруты. Контракт обязует тест реализовать какую-либо логику, например, проверку передачи валидных данных, параметров и т.д. Далее есть подробное описание контрактов
'contracts' => [
CodeContract::class => CodeContractResolver::class,
DocIgnoreContract::class => DocIgnoreResolver::class,
FakeStorageContract::class => FakeStorageResolver::class,
InvalidateCodeContract::class => InvalidateContractResolver::class,
InvalidateContract::class => InvalidateContractResolver::class,
InvalidParametersCodeContract::class => InvalidParametersCodeResolver::class,
InvalidParametersContract::class => InvalidParametersContractResolver::class,
JobContract::class => JobResolver::class,
MockContract::class => MockResolver::class,
NotificationContract::class => NotificationResolver::class,
ParametersContract::class => ParametersContractResolver::class,
SeedContract::class => SeedResolver::class,
ValidateContract::class => ValidateContractResolver::class,
ResponseContract::class => ResponseContractResolver::class,
],
Определяем какой миделвар отвечает за авторизацию в группе.
'auth_middleware' => 'auth:sanctum',
Далее мы определяем ресолверы для получения дефолтного кода
'code' => [ 'valid_data' => ValidDataCodeResolver::class, 'invalid_data' => InvalidDataCodeResolver::class, 'invalid_route_params' => InvalidRouteParamsResolver::class, ],
Затем для каждого пользователя нужно определить стратегию авторизации
'auth' => [ 'guest' => WithoutAuthStrategy::class, 'user' => SanctumAuthStrategy::class, ],
Это примеры из дефолтной группы. Параметры дефолтной группы применяютсвя к каждой группе, но в каждой группе мы можем переопределять эти данные.
Переходим к конфигурации по созданию документаии
'docs' => [ 'renderer' => Renderer::SWAGGER, 'routes' => [ 'middlewares' => [], 'ui' => [ 'name' => 'docs.ui', 'path' => 'api/documentation', 'middlewares' => [], ], 'scheme' => [ 'name' => 'docs.scheme', 'path' => 'api/documentation/scheme', 'middlewares' => [], ], ], 'openapi' => '3.0.0', 'info' => [ 'title' => 'API documentation', 'description' => 'API documentation', 'version' => '1.0.0', 'termsOfService' => 'https://example.com/terms', 'contact' => [ 'name' => 'example', 'url' => 'https://example.com', 'email' => 'example@mail.ru', ], 'license' => [ 'name' => 'CC Attribution-ShareAlike 4.0 (CC BY-SA 4.0)', 'url' => 'https://openweathermap.org/price', ], ], 'servers' => [ [ 'url' => env('APP_URL'), 'description' => 'Server for testing', ], ], 'auth' => [ 'security_scheme' => 'sanctum', 'security_schemes' => [ 'passport' => [ 'type' => 'http', 'scheme' => 'bearer', 'bearerFormat' => 'JWT', 'in' => 'header', 'name' => 'Authorization', 'description' => 'Use the Bearer token issued by Passport.', ], 'sanctum' => [ 'type' => 'http', 'scheme' => 'bearer', 'bearerFormat' => 'Token', 'in' => 'header', 'name' => 'Authorization', 'description' => 'Use Sanctum personal access token.', ], 'api_key_header' => [ 'type' => 'apiKey', 'in' => 'header', 'name' => 'X-API-Key', 'description' => 'Custom API key via header.', ], 'jwt' => [ 'type' => 'http', 'scheme' => 'bearer', 'bearerFormat' => 'JWT', 'description' => 'JWT authentication.', ], ], ], 'storage' => [ 'driver' => 'local', 'data' => [ 'path' => 'swagger/data/', ], 'docs' => [ 'name' => 'api-docs', 'path' => 'docs/api-docs/', ], ], 'collectors' => [ GroupCollector::class, CodeCollector::class, MethodCollector::class, PathCollector::class, ContentTypeCollector::class, AuthCollector::class, SummaryCollector::class, DescriptionCollector::class, RequestCollector::class, ResponseCollector::class, ], 'dto' => OpenApi::class, 'processors' => [ PathProcessor::class, MethodProcessor::class, GroupProcessor::class, AuthProcessor::class, SummaryProcessor::class, DescriptionProcessor::class, ResponseProcessor::class, RequestProcessor::class, ], ],
Выбираем какой рендерер будет использоваться при отображении документации. Сейчас доступен только swagger, но дальше их будет больше
'renderer' => Renderer::SWAGGER,
Можем определить данными маршрутов для ui схемы и ее исходных данных. Можем определить миделвары для обоих маршрутов и каждого по отдельности, а также имена и пути маршрутов.
'routes' => [ 'middlewares' => [], 'ui' => [ 'name' => 'docs.ui', 'path' => 'api/documentation', 'middlewares' => [], ], 'scheme' => [ 'name' => 'docs.scheme', 'path' => 'api/documentation/scheme', 'middlewares' => [], ], ],
Далее определяем версию OpenApi данные с основной информации и сервера
'openapi' => '3.0.0', 'info' => [ 'title' => 'API documentation', 'description' => 'API documentation', 'version' => '1.0.0', 'termsOfService' => 'https://example.com/terms', 'contact' => [ 'name' => 'example', 'url' => 'https://example.com', 'email' => 'example@mail.ru', ], 'license' => [ 'name' => 'CC Attribution-ShareAlike 4.0 (CC BY-SA 4.0)', 'url' => 'https://openweathermap.org/price', ], ], 'servers' => [ [ 'url' => env('APP_URL'), 'description' => 'Server for testing', ], ],
Затем переходим к авторизации. У нас уже есть список готовых схем авторизации, нужно выбрать нужную или добавить новые при необходимости
'auth' => [ 'security_scheme' => 'sanctum', 'security_schemes' => [ 'passport' => [ 'type' => 'http', 'scheme' => 'bearer', 'bearerFormat' => 'JWT', 'in' => 'header', 'name' => 'Authorization', 'description' => 'Use the Bearer token issued by Passport.', ], 'sanctum' => [ 'type' => 'http', 'scheme' => 'bearer', 'bearerFormat' => 'Token', 'in' => 'header', 'name' => 'Authorization', 'description' => 'Use Sanctum personal access token.', ], 'api_key_header' => [ 'type' => 'apiKey', 'in' => 'header', 'name' => 'X-API-Key', 'description' => 'Custom API key via header.', ], 'jwt' => [ 'type' => 'http', 'scheme' => 'bearer', 'bearerFormat' => 'JWT', 'description' => 'JWT authentication.', ], ], ],
Далее данные по хранилищу. Определяем драйвер, место хранения данных по тестам и место и имя файла документации.
'storage' => [ 'driver' => 'local', 'data' => [ 'path' => 'swagger/data/', ], 'docs' => [ 'name' => 'api-docs', 'path' => 'docs/api-docs/', ], ],
Определяем коллекторы. Коллектор - это класс, который собирает данные по тестам и пишет рещультат в массив под определнным ключом.
'collectors' => [
GroupCollector::class,
CodeCollector::class,
MethodCollector::class,
PathCollector::class,
ContentTypeCollector::class,
AuthCollector::class,
SummaryCollector::class,
DescriptionCollector::class,
RequestCollector::class,
ResponseCollector::class,
],
далее мы определяем класс, который будет наполняться данными и из которого будет сформированна документация
'dto' => OpenApi::class,
Затем определяем процессоры и их порядок. Процессор - анализриует собранные коллекторами данные и дополняет ими класс документации
'processors' => [
PathProcessor::class,
MethodProcessor::class,
GroupProcessor::class,
AuthProcessor::class,
SummaryProcessor::class,
DescriptionProcessor::class,
ResponseProcessor::class,
RequestProcessor::class,
],
После определения конфигурации переходим к созданию базовых классов по группам. Для каждой группы в каталоге тестов будет создан свой подкаталог и базовый класс для тестов
php artisan testrine:init
Затем создаем сами тесты. Для этого вызываем команду
php artisan testrine:tests
Команда покрывает маршруты тестами. Если нужно перезаписть можно использовать флаг -R
Либо можно использовать команду
php artisan testrine:make
Для создания одного файла тестов
После создания тестов запускаем их
php artisan test
Затем анализируем собранные файлы
php artisan testrine:parse
Для удаления собранных файлов используем команду
php artisan testrine:destroy
Можно использовать команду, которая объединяет в себе запуск тестов, анализ собранных данных и удлаение собранных данных
php artisan testrine:generate
Могут быть ситуации, когда нам нужно указать значение каких либо параметров маршрутов или данных в теле запроса или указать дефолтный код. Для этого нужно использовать бинды через класс DkDev\Testrine\Testrine и используем код билдер. Далее оно будет более детально описан.
Создание значений параметров маршрута. Мы определяем маршрут или можно для всех задать, для этого используем *. И значение, можно использовать билдер, можно просто передать строку
Testrine::binds()->pushValid( routeName: 'api.verification.verify', key: 'id', value: Builder::make()->method('getUser')->property('id'), );
создание невалидного параметра маршрута
Testrine::binds()->pushInvalid( routeName: 'api.verification.verify', key: 'id', value: Builder::make()->method('getUser')->property('id'), );
создание дефолтного кода. Определяем ресолвер, роут и код
Testrine::binds()->setDefaultCode( resolver: ValidDataCodeResolver::class, routeName: 'api.auth.logout', value: 204 );
создание дефолтного валидного значения. определяем роут/или для всех, ключ параметра и значение
Testrine::binds()->setDefaultValue( routeName: 'api.auth.login', key: 'email', value: Builder::make()->method('getUser')->property('email'), );
Контракты.
AssertContract. Применяется, когда нужно использовать сделать дополнительные проверки. Необходимо реализовать метод assert. Метод принимает текущий тест и ключ пользователя из-под которого тест выполняется.
public function assert(TestResponse $test, string $userKey): void { // todo }
CodeContract. Применяется, когда нужно переопределить дефолтные успешные коды ответа. Необходимо реализовать метод codes, возвращает ассоциативный массив, где ключ это пользователь, а значение это код
public function codes(): array { return [ 'guest' => 200, 'user' => 200, ]; }
DocIgnoreContract. Применяется, когда нужно проигнорировать в документации этот тест. Ресолвер этого контракта имеет трейт HasContractRoutes, это значит, что для автоиспользования этого контракта для ресолвера можно задать маршруты.
Testrine::binds()->setContractRoutes( contract: DocIgnoreContract::class, routes: [ 'api.home.index', ] );
FakeStorageContract. Применяется, когда нужно вызвать Storage::fake() до теста. Ресолвер этого контракта имеет трейт HasContractRoutes.
InvalidateCodeContract. Применяется. когда нужно использовать иной от дефолтного кода для невалидных данных. Нужно реализовать метод invalidDataCode
public function invalidDataCode(): int { return 301; }
InvalidateContract. Контракт используется, если нужно проверить передачу невалидных данных. Необходимо реализовать метод invalidData.
public function invalidData(): array { return [ 'name' => 123, 'age' => 'fake' ] }
InvalidParametersCodeContract. Применяется, когда нужно переопределить дефолитные коды для невалидных параметров маршрута. Нужно реализовать метод codesForInvalidParameters.
public function codesForInvalidParameters(): array { return [ 'guest' => 403, 'user' => 404, ]; }
InvalidParametersContract. Применяется, когда нужно проверить передачу невалидных параметров маршрута. Необходимо реализовать метод invalidParameters
public function invalidParameters(): array { return [ 'post' => 'sadas' ]; }
JobContract. Применяется, если нужно проверить работу джоб. Queue::fake() уже будет заранее вызван. Ресолвер этого контракта имеет трейт HasContractRoutes. Необходимо реализовать метод jobs, в котором проверить вызов джоб
public function jobs(): void { }
MockContract. Применяется, если нужно вызвать моки над классами. Ресолвер этого контракта имеет трейт HasContractRoutes. Необходимо реализовать метод mockAction, где делать все моки
public function mockAction(): void { }
NotificationContract. Применяется, если нужно проверить вызов увеодмлений. Ресолвер этого контракта имеет трейт HasContractRoutes. Нужно реализовать метод notifications
public function notifications(): void { }
ParametersContract.Если нужно проверить передачу валидных параметров маршрута.
public function parameters(): array { return [ 'post' => 1 ]; }
ResponseContract. Если нужно проверить структуру ответа. Необходимо реализовать метод getResponseStructure
public function getResponseStructure(): array { return [ 'data' => [ 'id', 'name', ] ]; }
SeedContract. Если перед тестом нужно вызвать сидеры. Ресолвер этого контракта имеет трейт HasContractRoutes. Нужно реализовать dbSeed
public function dbSeed(): void { }
ValidateContract. Если нужно проверить передачу валидных данных
public function validData(): array { return [ 'name' => 'fake_name', 'age' => 21 ]; }
Для формирования валидных данных используется генератор на основе правил валидации. Для различных правил есть свой обработчик. Правила можно расширять. Для этого нужно создать класс наследник класс DkDev\Testrine\ValidData\Rules\BaseRule и реализовать его абстрактные методы getPriority, hasThisRule и getValue. Пример реализации правила 'email'.
<?php declare(strict_types=1); namespace DkDev\Testrine\RequestPayload\Rules; use DkDev\Testrine\Enums\ValidData\RulePriority; class EmailRule extends BaseRule { public function getPriority(): RulePriority { return RulePriority::MEDIUM; } public function hasThisRule(): bool { return in_array('email', $this->rules, true); } public function getValue(): string { return 'fake()->email()'; } }
Затем нужно зарегистрировать новое правило.
Testrine::rules()->add(NewRule::class);
Также можно полностью переопределить правила, очистить, получить список правил
Testrine::rules()->set([ RequiredRule::class, EmailRule::class, ]); Testrine::rules()->clear(); $rules = Testrine::rules()->list();
Также можно кастомизировать стратегии авторизации, ресолверы контрактов, коллекторы, процессоры, ресолверы кодов и ClassNameBuilder, который автоматически формирует название класса. Для этого делаем бинды обработчика, обратныый вызов всегда получает текущий класс, для которого переопределяем логику.
Testrine::binds()->setHandler(ClassNameBuilder::class, function (ClassNameBuilder $builder) { // todo });
Также мы можем задать обработчики для различных событий
Testrine::handlers()->afterDestroy(function () { // todo }); Testrine::handlers()->beforeDestroy(function () { // todo }); Testrine::handlers()->afterGeneration(function () { // todo }); Testrine::handlers()->beforeGeneration(function () { // todo }); // сработает только если тесты будут вызваны через testrine:generate Testrine::handlers()->afterTests(function () { // todo }); // сработает только если тесты будут вызваны через testrine:generate Testrine::handlers()->beforeTests(function () { // todo });
CodeBuilder. Через него мы можем:
- обратиться к текущему тесту
- вызвать свойство
- вызвать свойство с null-safe оператором
- вызвать метод
- вызвать метод с null-safe оператором
- вызвать функцию
- вызвать статическую функцию класса
- просто сделать возврат строки
use DkDev\Testrine\CodeBuilder\Builder; // строитель начнет с $this Builder::make(); // обращаемся к методу getUser и получаем свойство email. Код будет следующим $this->getUser()->email; Builder::make()->method('getUser')->property('email'); // тоже самое, но с null-safe оператором $this?->getUser()?->email; Builder::make()->safeMethod('getUser')->safeProperty('email'); // вызов функции шифрования почты пользователя. Результат - sha1($this->getUser()->email); Builder::make('')->func('sha1', Builder::make()->method('getUser')->property('email')), // возврат строки. Результат - 'password' Builder::make('')->raw('password')
统计信息
- 总下载量: 27
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 0
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2025-12-17