带有约束的 Symfony2 单元测试表单


Symfony2 Unit Testing Forms with constraints

>我有一个没有连接到实体但确实有约束的表单,如下所示:http://symfony.com/doc/current/book/forms.html#adding-validation

ContactBundle''Tests''Form''Type''TestedTypeTest::testSubmitValidData Symfony''Component''OptionsResolver''Exception''InvalidOptionsException:"约束"选项不存在。已知选项包括:"操作"、"吸引"、"auto_initialize"、"block_name"、"by_reference"、"复合"、"数据"、"data_class"、"禁用"、"empty_data"、"error_bubbling"、"inherit_data"、"标签"、"label_attr"、"映射"、"max_length"、"方法"、"模式"、"post_max_size_message"、"property_path"、"read_only"、"必需"、"translation_domain"、"修剪"、"虚拟"

以下是表格的一部分:

class ContactType extends AbstractType
{
/**
 * Build the form
 * @param 'Symfony'Component'Form'FormBuilderInterface $builder BuilderInterface
 * @param array $aOption Array of options
 */
public function buildForm(FormBuilderInterface $builder, array $aOption)
{
    ///..
    $builder->add('name', 'text', array(
                'constraints' => array(
                    new NotBlank(),
                    new Length(array('min' => 3)),
                ),
            ))
            ->add('address', 'textarea', array(
                'required' => false
            ))
            //..
            ;
    //..

这是单元测试

class TestedTypeTest extends TypeTestCase
{
public function testSubmitValidData()
{
    $formData = array(
        'name' => 'Test Name',
        'address' => '',
    );
    $type = new ContactType();
    $form = $this->factory->create($type, $formData);
    $form->submit($formData);
    $this->assertTrue($form->isSynchronized());
    $view = $form->createView();
    $children = $view->children;
    foreach (array_keys($formData) as $key) {
        $this->assertArrayHasKey($key, $children);
    }
}
}

我猜这是测试而不是表单的问题,因为表单按预期工作。我不确定我应该改变什么。任何帮助或建议都会很方便

谢谢

我认为问题是您还将formData传递给工厂的创建方法。

只需通过$form->submit($formData)传递表单数据应该没问题,就像您在代码中已经拥有

的那样

更多文档: http://symfony.com/doc/current/cookbook/form/unit_testing.html

编辑:

好吧,似乎您必须在测试设置中将验证器/附件扩展添加到工厂对象中,如此处所述 http://symfony.com/doc/current/cookbook/form/unit_testing.html#adding-custom-extensions

是的,Rob是对的,我需要添加验证器/约束扩展。这样:

 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();
}
//..

我建议你覆盖TypeTestCase::getExtensions()方法

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;
    }

测试类中添加方法createValidator():

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

不需要重写TypeTestCase::getTypeExtensions(),因为ValidatorExtension类已经有一个loadTypeExtensions()方法。

然后,您的测试方法可以如下所示:

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

您可以测试无效数据:

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']);
}

来源:

  • 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

这个问题在Symfony 4.4中仍然会出现,但它很容易解决。

必须将ValidatorExtensionTrait添加到运行测试的类中。

use Symfony'Component'Form'Test'TypeTestCase;
class TestedTypeTest extends TypeTestCase
{
    use 'Symfony'Component'Form'Test'Traits'ValidatorExtensionTrait;
    public function testSubmitValidData()
    {
        $formData = [...];
        $type = new ContactType();
        $form = $this->factory->create($type, $formData);
    }
}

该特征负责添加使"约束"选项可用所需的扩展。