Symfony2 Unit Testing Forms with constraints

Yes Rob was right I needed to add validator/constraint extensions. Like so:

 class TestedTypeTest extends TypeTestCase
 {

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

    $validator = $this->getMock('\Symfony\Component\Validator\Validator\ValidatorInterface');
    $validator->method('validate')->will($this->returnValue(new ConstraintViolationList()));
    $formTypeExtension = new FormTypeValidatorExtension($validator);
    $coreExtension = new CoreExtension();

    $this->factory = Forms::createFormFactoryBuilder()
            ->addExtensions($this->getExtensions())
            ->addExtension($coreExtension)
            ->addTypeExtension($formTypeExtension)
            ->getFormFactory();
}
//..

I think the problem is that you also pass the formData to the factory's create method.

It should be fine to just pass the form data via $form->submit($formData) like you already have in your code

More docs: http://symfony.com/doc/current/cookbook/form/unit_testing.html

EDIT:

Well it seems you have to add the validator/constaint extensions to the factory object in your test setup like stated here http://symfony.com/doc/current/cookbook/form/unit_testing.html#adding-custom-extensions


I suggest you to override the TypeTestCase::getExtensions() method

use Symfony\Component\Form\Extension\Core\CoreExtension;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
use Symfony\Component\Form\Form;
use Symfony\Component\Translation\IdentityTranslator;
use Symfony\Component\Validator\ConstraintValidatorFactory;
use Symfony\Component\Validator\Context\ExecutionContextFactory;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
use Symfony\Component\Validator\Tests\Fixtures\FakeMetadataFactory;
use Symfony\Component\Validator\Validator\RecursiveValidator;

....

   public function getExtensions()
    {
        $extensions = parent::getExtensions();
        $metadataFactory = new FakeMetadataFactory();
        $metadataFactory->addMetadata(new ClassMetadata(  Form::class));
        $validator = $this->createValidator($metadataFactory);

        $extensions[] = new CoreExtension();
        $extensions[] = new ValidatorExtension($validator);

        return $extensions;
    }

add the method createValidator() in your test class :

    protected function createValidator(MetadataFactoryInterface $metadataFactory, array $objectInitializers = array())
    {
        $translator = new IdentityTranslator();
        $translator->setLocale('en');
        $contextFactory = new ExecutionContextFactory($translator);
        $validatorFactory = new ConstraintValidatorFactory();
        return new RecursiveValidator($contextFactory, $metadataFactory, $validatorFactory, $objectInitializers);
    }

No need to override TypeTestCase::getTypeExtensions() because ValidatorExtension class already has a loadTypeExtensions() method.

Then, your test method can be like this :

public function testSubmitValidData()
{
    $formData = array(
        'name' => 'Test Name',
        'address' => '',
    );
    $form = $this->factory->create(ContactType::class);
    $form->submit($formData);
    $this->assertTrue($form->isValid());
    $this->assertTrue($form->isSynchronized());

    $view = $form->createView();
    $children = $view->children;

    foreach (array_keys($formData) as $key) {
        $this->assertArrayHasKey($key, $children);
    }
}

You can test invalid data :

public function testSubmitInvalidValidData()
{
    $formData = array(
        'name' => 'T',
    );
    $form = $this->factory->create(ContactType::class);
    $form->submit($formData);
    $this->assertFalse($form->isValid());
    $this->assertFalse($view->children['name']->vars['valid']);

}

sources :

  • http://symfony.com/doc/current/cookbook/form/unit_testing.html#adding-custom-extensions

  • https://github.com/symfony/validator/blob/master/Tests/Validator/RecursiveValidatorTest.php#L25