My validator looks like:
<?php
declare(strict_types=1);
namespace App\Infrastructure\Domain\Model\Company\Validator;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
use Symfony\Contracts\Translation\TranslatorInterface;
final class ContainsUnsupportedSymbolsValidator extends ConstraintValidator
{
public function __construct(
private TranslatorInterface $translator
)
{
}
public function validate($value, Constraint $constraint): void
{
if (!$constraint instanceof ContainsUnsupportedSymbols) {
throw new UnexpectedTypeException($constraint, ContainsUnsupportedSymbols::class);
}
// custom constraints should ignore null and empty values to allow
// other constraints (NotBlank, NotNull, etc.) to take care of that
if (null === $value || '' === $value) {
return;
}
if (!is_string($value)) {
// throw this exception if your validator cannot handle the passed type so that it can be marked as invalid
throw new UnexpectedValueException($value, 'string');
}
if (!preg_match('/^\+?[0-9]{3}-?[0-9]{6,12}$/', $value, $matches)) {
// the argument must be a string or an object implementing __toString()
$this->context->buildViolation(
$this->translator->trans($constraint->message, ['{phone}' => $value])
)->addViolation();
}
}
}
The test I wrote for valid case is:
<?php
declare(strict_types=1);
namespace App\Tests\Unit\Infrastructure\Domain\Model\Company\Validator;
use App\Infrastructure\Domain\Model\Company\Validator\ContainsUnsupportedSymbols;
use App\Infrastructure\Domain\Model\Company\Validator\ContainsUnsupportedSymbolsValidator;
use App\Tests\Unit\UnitTestCase;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
final class ContainsUnsupportedSymbolsValidatorTest extends UnitTestCase
{
public function testValidatorWithValidPhone():void
{
$phone = "+359883202020";
$translationMock = $this->createMock(TranslatorInterface::class);
$constraint = new ContainsUnsupportedSymbols();
$constraintValidator = new ContainsUnsupportedSymbolsValidator($translationMock);
$this->assertEmpty($constraintValidator->validate($phone, $constraint));
}
}
Is it correct to test with assertEmpty when the validate method return type is void?
And how to test with incorrect data? Tried to mock the context and both buildViolation and addViolation methods but got error:
Error: Call to a member function buildViolation() on null
This is what I tried with incorrect phone
public function testValidatorWithInvalidPhone():void
{
$phone = "+359()883202020";
$translationMock = $this->createMock(TranslatorInterface::class);
$contextMock = $this->createMock(ExecutionContextInterface::class);
$contextMock->expects($this->once())
->method('buildViolation')
->with('message');
$contextMock->expects($this->once())
->method('addViolation');
$constraint = new ContainsUnsupportedSymbols();
$constraintValidator = new ContainsUnsupportedSymbolsValidator($translationMock);
$this->assertEmpty($constraintValidator->validate($phone, $constraint));
}