Unit testing custom validator

Viewed 43

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));
    }
1 Answers

Your context is null. You have to initialize the ConstraintValidator extended by your custom validator. The question has found an answer here: Unit testing a custom symfony constraint

In short here is an example https://github.com/symfony/symfony/blob/6.2/src/Symfony/Component/Validator/Tests/Constraints/BlankValidatorTest.php

In the example the test class extends ConstraintValidatorTestCase that defines a context (look here: https://github.com/symfony/validator/blob/6.1/Test/ConstraintValidatorTestCase.php)

Related