Модульный тест для FormErrorSerializer в Symfony 4 - всегда действительная форма - PullRequest
0 голосов
/ 28 ноября 2018

Я пытаюсь написать модульный тест для 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 голосов
/ 29 ноября 2018

Хорошо, я смог сделать это двумя разными способами.

Первым решением было загрузить валидатор в методе 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; - это специальный контейнер, который предоставляет частные сервисы при тестировании.

Подробнее об этом здесь .

...