Unit testing custom validation constraint in Symfony 2.1 but without accessing the container?

How can i unit test ContainsItalianVatinValidator custom validator, but w*ithout accessing the container* and the validator service (and thus, create a stub object)?

class ContainsItalianVatinValidator extends ConstraintValidator
{
    /**
     * @param mixed $value
     * @param \Symfony\Component\Validator\Constraint $constraint
     */
    public function validate($value, Constraint $constraint)
    {    
        if (!preg_match('/^[0-9]{11}$/', $value, $matches)) {
            $this->context->addViolation($constraint->message, array(
                '%string%' => $value
            ));
        }

        // Compute and check control code
        // ...
    }
}

In my test case i know i should access the ConstraintViolationList, but i don't know how to do it from the validator itself:

class ContainsItalianVatinValidatorTest extends \PHPUnit_Framework_TestCase
{
    public function testEmptyItalianVatin()
    {
        $emptyVatin = '';
        $validator  = new ContainsItalianVatinValidator();
        $constraint = new ContainsItalianVatinConstraint();

        // Do the validation
        $validator->validate($emptyVatin, $constraint);

        // How can a get a violation list and call ->count()?
        $violations = /* ... */;

        // Assert
        $this->assertGreaterThan(0, $violations->count());
    }
}

When you take a look at the parent class of the validator Symfony\Component\Validator\ConstraintValidator you see that there is a method called initialize which takes an instance of Symfony\Component\Validator\ExecutionContext as argument.

After you created the validator you can call the initialize method and pass a mock context to the validator. You don't have to test if the addViolation method works correctly, you only have to test if it is called and if it is called with the correct parameters. You can do that with the mock functionality of PHPUnit.

...
$validator  = new ContainsItalianVatinValidator();
$context = $this->getMockBuilder('Symfony\Component\Validator\ExecutionContext')-> disableOriginalConstructor()->getMock();

$context->expects($this->once())
    ->method('addViolation')
    ->with($this->equalTo('[message]'), $this->equalTo(array('%string%', '')));

$validator->initialize($context);

$validator->validate($emptyVatin, $constraint);
...

In this code you have to replace [message] with the message stored in $constraint->message.

Actually, this question is more related to PHPUnit than to Symfony. You may find the Test Doubles chapter of the PHPUnit documentation interesting.

How to Create a custom Validation Constraint (Symfony Docs), How to Create a custom Validation Constraint: You can create a custom constraint by Service Container · Sessions · Testing · Translation (i18n) · Validation First you need to create a Constraint class and extend Constraint : to the validator's context property and a value will be considered valid if it causes no violations. How to Create a custom Validation Constraint¶. You can create a custom constraint by extending the base constraint class, Constraint.As an example you're going to create a simple validator that checks if a string contains only alphanumeric characters.

Updated for Symfony 2.5+. Add a test for each possible message that the validate() method in your validator might add with the value that would trigger that message.

<?php

namespace AcmeBundle\Tests\Validator\Constraints;

use AcmeBundle\Validator\Constraints\SomeConstraint;
use AcmeBundle\Validator\Constraints\SomeConstraintValidator;

/**
 * Exercises SomeConstraintValidator.
 */
class SomeConstraintValidatorTest extends \PHPUnit_Framework_TestCase
{
    /**
     * Configure a SomeConstraintValidator.
     *
     * @param string $expectedMessage The expected message on a validation violation, if any.
     *
     * @return AcmeBundle\Validator\Constraints\SomeConstraintValidator
     */
    public function configureValidator($expectedMessage = null)
    {
        // mock the violation builder
        $builder = $this->getMockBuilder('Symfony\Component\Validator\Violation\ConstraintViolationBuilder')
            ->disableOriginalConstructor()
            ->setMethods(array('addViolation'))
            ->getMock()
        ;

        // mock the validator context
        $context = $this->getMockBuilder('Symfony\Component\Validator\Context\ExecutionContext')
            ->disableOriginalConstructor()
            ->setMethods(array('buildViolation'))
            ->getMock()
        ;

        if ($expectedMessage) {
            $builder->expects($this->once())
                ->method('addViolation')
            ;

            $context->expects($this->once())
                ->method('buildViolation')
                ->with($this->equalTo($expectedMessage))
                ->will($this->returnValue($builder))
            ;
        }
        else {
            $context->expects($this->never())
                ->method('buildViolation')
            ;
        }

        // initialize the validator with the mocked context
        $validator = new SomeConstraintValidator();
        $validator->initialize($context);

        // return the SomeConstraintValidator
        return $validator;
    }

    /**
     * Verify a constraint message is triggered when value is invalid.
     */
    public function testValidateOnInvalid()
    {
        $constraint = new SomeConstraint();
        $validator = $this->configureValidator($constraint->someInvalidMessage);

        $validator->validate('someInvalidValue', $constraint);
    }

    /**
     * Verify no constraint message is triggered when value is valid.
     */
    public function testValidateOnValid()
    {
        $constraint = new SomeConstraint();
        $validator = $this->configureValidator();

        $validator->validate('someValidValue', $constraint);
    }
}

symfony2 - Symfony unit testing, Answer (b): Create the Validator inside the Unit Test (Symfony 2.0) Say for example you want to test the Type constraint from Symfony and it's We end up rolling your own base test case to access the dependency container from within a test case. symfony2 - Unit testing custom validation constraint in Symfony 2.1 bu. Don't test the validation: it is applied by a listener that is not active in the test case and it relies on validation configuration. Instead, unit test your custom constraints directly or read how to add custom extensions in the last section of this page.

Updated for 3.4:

I've put the context creation in a trait, so we can reuse it for all our custom constraints.

class SomeConstraintValidatorTest extends TestCase
{
    use ConstraintValidationTrait;

    /** @var SomeConstraint */
    private $constraint;

    protected function setUp()
    {
        parent::setUp();

        $this->constraint = new SomeConstraint();
    }

    public function testValidateOnInvalid()
    {
        $this->assertConstraintRejects('someInvalidValue', $this->constraint);
    }

    public function testValidateOnValid()
    {
        $this->assertConstraintValidates('someValidValue', $this->constraint);
    }
}

The trait:

<?php

use PHPUnit\Framework\MockObject\MockObject;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Context\ExecutionContext;

trait ConstraintValidationTrait
{
    /**
     * The assertion is done in the mock.
     *
     * @param mixed $value
     */
    public function assertConstraintValidates($value, Constraint $constraint): void
    {
        $validator = $this->createValidator($constraint, true);
        $validator->validate($value, $constraint);
    }

    /**
     * The assertion is done in the mock.
     *
     * @param mixed $value
     */
    public function assertConstraintRejects($value, Constraint $constraint): void
    {
        $validator = $this->createValidator($constraint, false);
        $validator->validate($value, $constraint);
    }

    /** This is the phpunit mock method this trait requires */
    abstract protected function createMock($originalClassName): MockObject;

    private function createValidator(Constraint $constraint, bool $shouldValidate): ConstraintValidator
    {
        $context = $this->mockExecutionContext($shouldValidate);

        $validatorClass = get_class($constraint) . 'Validator';

        /** @var ConstraintValidator $validator */
        $validator = new $validatorClass();
        $validator->initialize($context);

        return $validator;
    }

    /**
     * Configure a SomeConstraintValidator.
     *
     * @param string|null $expectedMessage The expected message on a validation violation, if any.
     *
     * @return ExecutionContext
     */
    private function mockExecutionContext(bool $shouldValidate): ExecutionContext
    {
        /** @var ExecutionContext|MockObject $context */
        $context = $this->createMock(ExecutionContext::class);

        if ($shouldValidate) {
            $context->expects($this->never())->method('addViolation');
        } else {
            $context->expects($this->once())->method('addViolation');
        }

        return $context;
    }
}

Using Custom Validators in Symfony Controllers, Unit testing custom validation constraint in Symfony 2.1 but without accessing the container? How can i unit test ContainsItalianVatinValidator custom validator,  A bit Ugly, but Easy: Callback + constraints¶ Normally, we add validation constraints to our model class (i.e. Event). However, as of Symfony 2.1, additional constraints can be added directly to the form key using a constraints option. Like with annotations, you can apply constraints to the whole object, or individual properties.

Testion controler method in symfony (phpUnit) - symfony - Python, #1 is the service container, and #3 is the Twig subsystem. Unit testing custom validation constraint in Symfony 2.1 but without accessing the container? To avoid the creation of all dependencies when writing tests and to really just unit-test what we want, we are going to use test doubles. Test doubles are easier to create when we rely on interfaces instead of concrete classes. Fortunately, Symfony provides such interfaces for core objects like the URL matcher and the controller resolver.

Custom Validation, Callback and Constraints > Question and , Unit testing custom validation constraint in Symfony 2.1 but without accessing the container?. How can i unit test ContainsItalianVatinValidator custom validator,  Unit Tests¶ A unit test is a test against a single PHP class, also called a unit. If you want to test the overall behavior of your application, see the section about Functional Tests. Writing Symfony unit tests is no different from writing standard PHPUnit unit tests.

Документация - Symfony, Custom Validation, Callback and Constraints¶ From Rafael: Hi, I am coding This means that you don't have access to the entity manager or any other However, as of Symfony 2.1, additional constraints can be added directly to This solution is a bit ugly because it lives in our Controller, so we can't re-use it or unit test it. Validation¶. Validation is a very common task in web applications. Data entered in forms needs to be validated. Data also needs to be validated before it is written into a database or passed to a web service.

Comments
  • I would extract the validation logic into a service and write a unit test for this service. Inside the validator class you check your constraint with the service and add a message if the validation fail. Thereby your validation logic is not coupled to the framework and more robust to future changes.
  • Amazing good explanation. The only thing i can't get is why, in your opinion, counting violations is wrong and why i should prefer to rely on the constraint message itself. Anyway, +1.
  • Why should you count the violations. At least in the code in your question there is only one call to addViolation. If that method is called one time exactly one violation will be added to the context (the unit tests of Symfony2 test that).
  • If there should be more calls to addViolation in the code you could add multiple $context->expects statements where each covers one different call to addViolation. Sadly PHPUnit does only offer two methods to count the number of calls of a method once and any. However, Mockery is a mock library that is compatible with PHPUnit and can count the number of method calls on a mock object.
  • Thanks, i'll go for checking calls to addViolation.
  • As of 2016 PHPUnit also supports $this->exactly($numberOfCalls) to count the calls to a mocked method.
  • What a great answer! Thank you very much!