Symfony 4中FormErrorSerializer的单元测试-始终有效的形式

时间:2018-11-27 23:04:26

标签: forms unit-testing symfony phpunit symfony4

我正在尝试为FormErrorSerializer编写单元测试,以将Symfony $ form-> getErrors()转换为可读数组。

我当前的方法是创建表单,为其提供数据,并查找验证错误,但表单始终有效。无论我提供什么数据来形成表单,我都不会出错。

在正常的REST请求/响应中,它运行良好,并且我收到了适当的错误消息。我需要帮助以获取单元测试中的错误消息。

namespace App\Tests\Unit;

use App\Form\UserType;
use App\Serializer\FormErrorSerializer;
use Symfony\Component\Form\Test\Traits\ValidatorExtensionTrait;
use Symfony\Component\Form\Test\TypeTestCase;
use Symfony\Component\Translation\Translator;


class FormErrorSerializerTest extends TypeTestCase
{
    /**
     * ValidatorExtensionTrait needed for invalid_options
     * https://github.com/symfony/symfony/issues/22593
     */
    use ValidatorExtensionTrait;

    public function testConvertFormToArray(){
        $form_data = [
            'email' => 'test',
            'plainPassword' => [
                'pass' => '1',
                'pass2' => '2'
            ]
        ];

        $translator = new Translator('de');

        $form = $this->factory->create(UserType::class);

        $form->submit($form_data);

        if( $form->isValid() ) {
            echo  "Form is valid"; exit;
        }

        $formErrorSerializer = new FormErrorSerializer($translator);

        $errors = $formErrorSerializer->convertFormToArray($form);

        print_r($errors); exit;
    }
}

在序列化器下面找到:

namespace App\Serializer;

use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Translation\TranslatorInterface;

/**
 * Serializes invalid Form instances.
 */
class FormErrorSerializer
{
    private $translator;

    public function __construct(TranslatorInterface $translator)
    {
        $this->translator = $translator;
    }

    public function convertFormToArray(FormInterface $data)
    {
        $form = $errors = [];

        foreach ($data->getErrors() as $error) {
            $errors[] = $this->getErrorMessage($error);
        }

        if ($errors) {
            $form['errors'] = $errors;
        }

        $children = [];
        foreach ($data->all() as $child) {
            if ($child instanceof FormInterface) {
                $children[$child->getName()] = $this->convertFormToArray($child);
            }
        }

        if ($children) {
            $form['children'] = $children;
        }

        return $form;
    }

    private function getErrorMessage(FormError $error)
    {
        if (null !== $error->getMessagePluralization()) {
            return $this->translator->transChoice(
                $error->getMessageTemplate(),
                $error->getMessagePluralization(),
                $error->getMessageParameters(),
                'validators'
            );
        }

        return $this->translator->trans($error->getMessageTemplate(), $error->getMessageParameters(), 'validators');
    }
}

1 个答案:

答案 0 :(得分:0)

好的,我能够以两种不同的方式来做到这一点。

第一个解决方案是在getExtensions方法中加载验证器。 TypeTestCase中的工厂不附带验证器。因此,不仅必须加载验证器,而且还必须显式指定验证。您可以使用symfony提供的方法指定验证,也可以直接将验证器指向YAML或xml文件。

public function getExtensions()
{
    $validator = (new ValidatorBuilder())
        ->addYamlMapping("path_to_validations.yaml")
        ->setConstraintValidatorFactory(new ConstraintValidatorFactory())
        ->getValidator();

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

    return $extensions;
}

但是,我没有使用上述方法。我提供了更好的解决方案。由于我的测试用例非常复杂(因为它需要多种服务),因此我选择了Symfony的KernelTestCase提供的特殊容器。它在测试中提供私有服务,并且它提供的工厂带有验证器和验证,就像在控制器中编写代码一样。您不需要显式加载验证器。在我的扩展KernelTestCase的最终测试下面找到。

namespace App\Tests\Unit\Serializer;

use App\Entity\User;
use App\Form\UserType;
use App\Serializer\FormErrorSerializer;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Translation\TranslatorInterface;

class FormErrorSerializerTest extends KernelTestCase
{
    /**
     * {@inheritDoc}
     */
    protected function setUp()
    {
        $kernel = self::bootKernel();
    }

    public function testConvertFormToArray_invalidData(){
        $form_data = [
            'email' => 'test',
            'plainPassword' => [
                'pass' => '1111',
                'pass2' => ''
            ]
        ];

        $user = new User();
        $user->setEmail($form_data['email']);
        $user->setPlainPassword($form_data['plainPassword']['pass']);

        $factory = self::$container->get(FormFactoryInterface::class);
        /**
         * @var FormInterface $form
         */
        $form = $factory->create(UserType::class, $user);

        $form->submit($form_data);

        $this->assertTrue($form->isSubmitted());
        $this->assertFalse($form->isValid());

        $translator = self::$container->get(TranslatorInterface::class);
        $formErrorSerializer = new FormErrorSerializer($translator);
        $errors = $formErrorSerializer->convertFormToArray($form);

        $this->assertArrayHasKey('errors', $errors['children']['email']);
        $this->assertArrayHasKey('errors', $errors['children']['plainPassword']['children']['pass']);
    }

    public function testConvertFormToArray_validData(){
        $form_data = [
            'email' => 'test@example.com',
            'plainPassword' => [
                'pass' => 'somepassword@slkd12',
                'pass2' => 'somepassword@slkd12'
            ]
        ];

        $user = new User();
        $user->setEmail($form_data['email']);
        $user->setPlainPassword($form_data['plainPassword']['pass']);

        $factory = self::$container->get(FormFactoryInterface::class);
        /**
         * @var FormInterface $form
         */
        $form = $factory->create(UserType::class, $user);

        $form->submit($form_data);

        $this->assertTrue($form->isSubmitted());
        $this->assertTrue($form->isValid());

        $translator = self::$container->get(TranslatorInterface::class);
        $formErrorSerializer = new FormErrorSerializer($translator);
        $errors = $formErrorSerializer->convertFormToArray($form);

        $this->assertArrayNotHasKey('errors', $errors['children']['email']);
        $this->assertArrayNotHasKey('errors', $errors['children']['plainPassword']['children']['pass']);
    }
}

请注意,Symfony 4.1具有一个特殊的容器,该容器可以获取私有服务。

self::$kernel->getContainer();不是特殊容器。它不会获取私有服务。

但是,self::$container;是一个特殊的容器,在测试中提供私有服务。

有关此here的更多信息。