Laravel: Как тестировать invokable правила
__invoke
.Вот простой пример:
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\InvokableRule;
class UppercaseRule implements InvokableRule
{
public function __invoke(string $attribute, mixed $value, Closure $fail): void
{
if (strtoupper($value) !== $value) {
$fail('The :attribute must be uppercase.');
}
}
}
Если хотите, чтобы правило не прошло валидацию, он должно выполнить переданное Замыкание $fail
и выдать сообщение об ошибке валидации.
Этот способ написания пользовательских правил валидации станет стандартным в Laravel 10. (Если вы пишете свои классы валидации используя старый синтаксис, не беспокойтесь, он всё ещё будет поддерживаться в Laravel 10).
Когда я написал свой первый вызываемый объект, я думал, как его протестировать. Придумал хорошее решение (спасибо Оливеру Найборо за подсказку правильного направления)
Сейчас я предпочитаю тестовый фреймворк Pest. В Pest есть способ написания пользовательского ожидания.
Вот пользовательское ожидание, которое я придумал:
expect()->extend('toPassWith', function(mixed $value) {
$rule = $this->value;
if (! $rule instanceof InvokableRule) {
throw new Exception('Value is not an invokable rule');
}
$passed = true;
$fail = function() use (&$passed) {
$passed = false;
};
$rule('attribute', $value, $fail);
expect($passed)->toBeTrue();
});
В это пользовательском ожидании мы будем вызывать наше правило и передавать ему наше собственное замыкание $fail
. Используя переменную $passed
, мы определяем была ли выполнена $fail
. Заметили, что &$passed
? Здесь мы передаём переменную $passed
по ссылке поэтому когда она изменится внутри замыкания, она так же изменится в нашем пользовательском ожидании.
Используя это пользовательское ожидание, тестирование пользовательских правил становится простым. Давайте посмотрим на это в действии.
it('will only accept fully uppercased values', function() {
$rule = new UppercaseRule();
expect($rule)->toPassWith('HELLO');
// используя not() you вы можете инвертировать ожидание
expect($rule)->not()->toPassWith('hello');
// вы можете связать ожидания
expect($rule)
->toPassWith('ANOTHER')
->toPassWith('TEST');
});
В реальном тестировании вы можете захотеть добавить больше случаев для проверки этого конкретного правила, но я надеюсь, что этот пример хорошо показывает, что toPassWith
делает тестирование вызываемых правил очень простым.
Мне особенно нравится, что наш тест написан на простом английском языке использующим этот синтаксис.
expect($rule)->toPassWith('HELLO');
Круто, правда?
Если хотите использовать это ожидание в своих тестах, можете выбрать наш новый пакет spatie/pest-expectations, который содержит пару удобных опциональных пользовательских ожиданий.