Symfony类型测试用例,错误:找不到类别“doctrine.orm.validator.unique”

relj7zay  于 2022-11-16  发布在  其他
关注(0)|答案(1)|浏览(94)

**意图:**我想测试School实体上是否有我想要的验证,为此我想编写一个扩展TypeTestCase的测试类
疑问/问题:

1.我要清 debugging 误Error: Class "doctrine.orm.validator.unique" not found
1.我想为元素的每个约束Assert错误消息。当我从模型中删除#[UniqueEntity('name')]时,问题1消失了,但Assertself::assertCount(1, $form->getErrors());仍然失败。这意味着$form->getErrors()没有名称为空的验证错误。
我正在尝试编写一个symfony测试一个带有DB实体的symfony Form类型,定义如下:

namespace App\Entity;

use App\Repository\SchoolRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;

#[ORM\Entity(repositoryClass: SchoolRepository::class)]
// >>>>>>> If I remove it problem 1 will be solved 
#[UniqueEntity('name')]
class School implements TenantAwareInterface
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private $id;

    #[Assert\NotBlank]
    #[ORM\Column(type: 'string', length: 255, unique: true)]
    private $name;
    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }
}

而形式是:

namespace App\Form;

use App\Entity\School;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class SchoolType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder->add('name');
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => School::class,
            'required' => false
        ]);
    }
}

测试:

namespace App\Tests\Integration\Form;

use App\Entity\School;
use App\Form\SchoolType;
use Doctrine\Persistence\ManagerRegistry;
use Mockery as m;
use Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
use Symfony\Component\Form\PreloadedExtension;
use Symfony\Component\Form\Test\Traits\ValidatorExtensionTrait;
use Symfony\Component\Form\Test\TypeTestCase;
use Symfony\Component\Validator\Validation;
use Symfony\Contracts\Translation\TranslatorInterface;

class SchoolTypeTest extends TypeTestCase
{
    use ValidatorExtensionTrait;

    protected function getExtensions(): array
    {
        $validator = Validation::createValidatorBuilder()
            ->enableAnnotationMapping()
            ->addDefaultDoctrineAnnotationReader()
            ->getValidator();

        $mockedManagerRegistry = m::mock(ManagerRegistry::class, ['getManagers' => []]);

        return [
            new ValidatorExtension($validator),
            new DoctrineOrmExtension($mockedManagerRegistry),
        ];
    }

    public function testValidationReturnsError()
    {
        $school = new School();
        $form = $this->factory->create(SchoolType::class, $school);

        $form->submit([]);

        self::assertTrue($form->isSynchronized());
        self::assertFalse($form->isValid());

        // >>>>>>> I want this to assert, problem 2
        self::assertCount(1, $form->getErrors());
    }
}
mklgxw1f

mklgxw1f1#

简而言之,我最后写了一个mocked UniqueEntity验证器,我添加了一些通用代码来简化其他表单类型的测试,如下所示:

测试的基础:

namespace App\Tests\Service;

use Doctrine\Persistence\ManagerRegistry;
use Mockery as m;
use Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator;
use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\Test\Traits\ValidatorExtensionTrait;
use Symfony\Component\Form\Test\TypeTestCase;
use Symfony\Component\Validator\Validation;

class AppTypeWithValidationTestCase extends TypeTestCase
{
    use ValidatorExtensionTrait;

    protected function getExtensions(): array
    {
        $mockedManagerRegistry = m::mock(
            ManagerRegistry::class,
            [
                'getManagers' => []
            ]
        );

        $factory = new AppConstraintValidatorFactory();
        $factory->addValidator(
            'doctrine.orm.validator.unique',
            m::mock(UniqueEntityValidator::class, [
                'initialize' => null,
                'validate' => true,
            ])
        );

        $validator = Validation::createValidatorBuilder()
            ->setConstraintValidatorFactory($factory)
            ->enableAnnotationMapping()
            ->addDefaultDoctrineAnnotationReader()
            ->getValidator();

        return [
            new ValidatorExtension($validator),
            new DoctrineOrmExtension($mockedManagerRegistry),
        ];
    }

    // *** Following is a helper function which ease the way to 
    // *** assert validation error messages
    public static function assertFormViewHasError(FormView $formElement, string $message): void
    {
        foreach ($formElement->vars['errors'] as $error) {
            self::assertSame($message, $error->getMessage());
        }
    }
}

一个接受验证器的约束验证器,我们需要它来添加UniqeEntity的(模拟)定义:

namespace App\Tests\Service;

use Symfony\Component\Validator\ConstraintValidatorFactory;
use Symfony\Component\Validator\ConstraintValidatorInterface;

class AppConstraintValidatorFactory extends ConstraintValidatorFactory
{
    public function addValidator(string $className, ConstraintValidatorInterface $validator): void
    {
        $this->validators[$className] = $validator;
    }
}

最后一个单元测试类:

<?php

declare(strict_types=1);

namespace App\Tests\Unit\Form;

use App\Entity\School;
use App\Form\SchoolType;
use App\Tests\Service\AppTypeWithValidationTestCase;

class SchoolTypeTest extends AppTypeWithValidationTestCase
{
    public function testValidationReturnsError() {
        $input = [
            // *** Note that 'name' is missing here
            'is_enabled' => true,
        ];
        
        $school = new School();
        $form = $this->factory->create(SchoolType::class, $school);

        $form->submit($input);

        self::assertTrue($form->isSynchronized());
        self::assertFalse($form->isValid());

        $view = $form->createView();

        self::assertFormViewHasError($view->children['name'], 'This value should not be blank.');
    }
}

相关问题