I would like to unit test a ValidationRule, but im not really sure how to instance and test the Closure.
<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class Uppercase implements ValidationRule
{
/**
* Run the validation rule.
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (strtoupper($value) !== $value) {
$fail('The :attribute must be uppercase.');
}
}
}
My test
public function test ()
{
$fail = null; // I dont know how to create this Closure
$validator = new Uppercase();
$validator->validate('myparam', 'myvalue', $fail);
// i need to assert here, $fail contains the failure message
}
You can get the message bag from the validator using $validator->messages() if you want to assert the actual message returned, but I don't tend to do that unless I'm asserting against a language file string. It would make the test brittle if someone wanted to change the wording of an error message.
This is what im using, since was not able to test from the way i would like.
Another option I have considered is to extend Rules from abstract BaseRule which trigger the Closure then validate the rules in classes as old way, just method which return bool. BaseRule will check if this method is passes or not and trigger the closue.
You could try this approach; setting your own Closure where the assertion is being made.
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
class UppercaseValidationRuleTest extends TestCase
{
/**
* A basic unit test example.
*/
public function test_example(): void
{
$fail = fn($message) => $this->assertEquals($message, 'The :attribute must be uppercase.');
$validator = new \App\Rules\UppercaseOnly();
$validator->validate('myparam', 'myvalue', $fail);
}
}