Testez unitairement un validateur personnalisé Symfony
Publié le 05/11/2022 • Actualisé le 05/11/2022
Dans ce bout de code, nous voyons comment tester unitairement un validateur personnalisé Symfony. On apprend tous les jours : j'ai fait ce snippet inspiré par diverses ressources sur le web, puis j'ai relu la documentation, et j'ai réalisé qu'il existait désormais un cas de test ConstraintValidatorTestCase
que je connaissais pas et que je n'avais jamais utilisé. Ce bout de code est toujours utile, au moins nous voyons comment utiliser des mocks. Dans le prochain snippet, nous verrons comme faire pareil avec le cas de test ConstraintValidatorTestCase
. La contrainte que j'utilise ici est factice, elle teste juste qu'une chaîne de caractères n'est pas égale à la valeur "coil". Avec ces tests unitaires, on obtient 100% de couverture de code sur notre contrainte personnalisée.
<?php
declare(strict_types=1);
namespace App\Tests\Unit\Validator;
use App\Entity\Article;
use App\Validator\NotCoil;
use App\Validator\NotCoilValidator;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Context\ExecutionContext;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
use Symfony\Component\Validator\Violation\ConstraintViolationBuilder;
final class NotCoilTest extends TestCase
{
/**
* You can put this method in a trait.
*/
private function initValidator(ConstraintValidator $validator, ?string $expectedMessage = null): void
{
$builder = $this->getMockBuilder(ConstraintViolationBuilder::class)
->disableOriginalConstructor()
->getMock();
$context = $this->getMockBuilder(ExecutionContext::class)
->disableOriginalConstructor()
->getMock();
if ($expectedMessage === null) {
// no violation expected
$context->expects(self::never())->method('buildViolation');
} else {
// a violation is expected
$builder->expects(self::once())->method('addViolation');
$context->expects(self::once())
->method('buildViolation')
->with(self::equalTo($expectedMessage))
->willReturn($builder);
}
/* @var ExecutionContext $context */
$validator->initialize($context);
}
/**
* Nominal cases, no validation error.
*/
public function testNotCoilValidatorSuccess(): void
{
$constraint = new NotCoil();
$validator = new NotCoilValidator();
$this->initValidator($validator);
$validator->validate('Foobar', $constraint);
$validator->validate('', $constraint);
}
/**
* Nominal case, a violation is raised.
*/
public function testNotCoilValidatorViolations(): void
{
$constraint = new NotCoil();
$validator = new NotCoilValidator();
$this->initValidator($validator, 'The value should not be COil');
$validator->validate('COil', $constraint);
}
/**
* Value type failure.
*/
public function testNotCoilValidatorUnexpectedValueException(): void
{
$constraint = new NotCoil();
$validator = new NotCoilValidator();
$this->expectException(UnexpectedValueException::class);
$validator->validate(new Article(), $constraint); // not the good value type!
}
/**
* Constraint type failure.
*/
public function testNotCoilValidatorUnexpectedTypeException(): void
{
$constraint = new NotCoil();
$validator = new NotCoilValidator();
$this->expectException(UnexpectedTypeException::class);
$validator->validate($constraint, new Length(['max' => 5])); // not the good constraint!
}
}
Plus sur Stackoverflow Lire la doc Snippet aléatoire