简体   繁体   English

如何在 Symfony 中测试一个字段为 EntityType 的表单

[英]How to test a Form having a field being EntityType in Symfony

(Using framework Symfony 4.4) (使用框架 Symfony 4.4)

I try to learn about how to test a Form having a field being an EntityType form field.我尝试了解如何测试具有作为 EntityType 表单字段的字段的表单。

See exemple bellow:请参见下面的示例:

class VaccinationType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('vaccine_brand_uid', EntityType::class, ['class' => ViewVaccineBrand::class])
                ->add('administration_date', DateTimeType::class, [
                      'widget' => 'single_text',
                      'model_timezone' => 'UTC',
                      'view_timezone' => 'UTC',
                  ]);
    }



    public function configureOptions(OptionsResolver $resolver)
    {
        parent::configureOptions($resolver);

        $resolver->setDefaults([
            'data_class' => VaccinationInput::class,
            'method' => 'POST',
            'csrf_protection' => false
        ]);
    }
}

As you can see the field vaccine_brand_uid is an EntityType field so it ensure the given value when submitting the form is part of the ViewVaccineBrand table.如您所见,字段 vaccine_brand_uid 是一个 EntityType 字段,因此它确保提交表单时给定的值是ViewVaccineBrand表的一部分。

Here's bellow the related VaccinationInput object:下面是相关的VaccinationInput object:

namespace App\Services\Domain\Vaccination\DTO;

use App\Entity\ViewVaccineBrand;
...

class VaccinationInput
{

    /**
     * @Assert\Type(type=ViewVaccineBrand::class, message="api_missingBrand")
     * @Assert\NotBlank(message="api_missingBrand")
     * @var ViewVaccineBrand|int
     */
    public $vaccine_brand_uid;
   
    /**
     * @Assert\DateTime(message="api_missingAdministrationDate")
     */
    public $administration_date;
}

Create a base class for testing form with EntityType fields创建一个基础 class 用于测试带有 EntityType 字段的表单

So while trying to create a test class for this form, I found this exemple in the Symfony repository: https://github.com/symfony/symfony/blob/4.4/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php因此,在尝试为此表单创建测试 class 时,我在 Symfony 存储库中找到了这个示例: https://github.com/symfony/symfony/blob/4.4/src/Symfony/Bridge/Doctrine/Tests/Form/Type /EntityTypeTest.php

So I copy/paste this class to adapt it for my own tests !所以我复制/粘贴这个 class 以适应我自己的测试!

And I adapt it a little so it is more reusable (adding the getTypes() function):我对其进行了一些调整,使其更易于重用(添加 getTypes() 函数):

/**
 * Inspired by https://github.com/symfony/symfony/blob/4.4/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php
 */
abstract class EntityTypeTestCase extends TypeTestCase
{
    /**
     * @var EntityManager
     */
    protected $em;

    /**
     * @var MockObject&ManagerRegistry
     */
    protected $emRegistry;

    protected function setUp(): void
    {
        $this->em = DoctrineTestHelper::createTestEntityManager();
        $this->emRegistry = $this->createRegistryMock('default', $this->em);

        parent::setUp();

        $schemaTool = new SchemaTool($this->em);

        $classes = array_map(fn($c) => $this->em->getClassMetadata($c), $this->registeredTypes());

        try {
            $schemaTool->dropSchema($classes);
        } catch (\Exception $e) {
        }

        try {
            $schemaTool->createSchema($classes);
        } catch (\Exception $e) {
        }
    }

    protected function tearDown(): void
    {
        parent::tearDown();

        $this->em = null;
        $this->emRegistry = null;
    }

    protected function getExtensions(): array
    {
        return array_merge(parent::getExtensions(), [
            new DoctrineOrmExtension($this->emRegistry),
        ]);
    }

    protected function createRegistryMock($name, $em): ManagerRegistry
    {
        $registry = $this->createMock(ManagerRegistry::class);
        $registry->expects($this->any())
            ->method('getManager')
            ->with($this->equalTo($name))
            ->willReturn($em);

        return $registry;
    }

    protected function persist(array $entities)
    {
        foreach ($entities as $entity) {
            $this->em->persist($entity);
        }

        $this->em->flush();
    }

    protected function getTypes()
    {
        return array_merge(parent::getTypes(), []);
    }

    /**
     * @return array An array of current registered entity type classes.
     */
    abstract protected function registeredTypes(): array;
}

Create a specific test class for my Vaccination form为我的疫苗接种表创建一个特定的测试 class

So I want to test my VaccinationType form, here's what I did bellow.所以我想测试我的 VaccinationType 表格,下面是我所做的。

~/symfony/tests/Service/Domain/Vaccination/Type/VaccinationTypeTest

<?php

namespace App\Tests\Service\Domain\Vaccination\Type;
...
class VaccinationTypeTest extends EntityTypeTestCase
{
    public function testWhenMissingBrandThenViolation()
    {
        $model = new VaccinationInput();
        $entity1 = (new ViewVaccineBrand())->setVaccineBrandName('test')->setIsActive(true)->setVaccineCode('code');
        $this->persist([$entity1]);

        // $model will retrieve data from the form submission; pass it as the second argument
        $form = $this->factory->create(VaccinationType::class, $model);

        $form->submit(['vaccine_brand_uid' => 2, 'administration_date' => DateTime::formatNow()]);

        $violations = $form->getErrors(true);
        $this->assertCount(1, $violations); // There is no vaccine brand for uid 2
    }

    protected function getTypes()
    {
        return array_merge(parent::getTypes(), [new EntityType($this->emRegistry)]);
    }

    protected function registeredTypes(): array
    {
        return [
            ViewVaccineBrand::class,
            ViewVaccineCourse::class,
        ];
    }

    protected function getExtensions(): array
    {
        $validator = Validation::createValidator();

        return array_merge(parent::getExtensions(), [
            new ValidatorExtension($validator),
        ]);
    }
}

Actual result实际结果

The actual result of the php bin/phpunit execution is as follow: php bin/phpunit执行的实际结果如下:

There was 1 error:有 1 个错误:

  1. App\Tests\Service\Domain\Vaccination\Type\VaccinationTypeTest::testWhenMissingBrandThenViolation Symfony\Component\Form\Exception\RuntimeException: Class "App\Entity\ViewVaccineBrand" seems not to be a managed Doctrine entity. App\Tests\Service\Domain\Vaccination\Type\VaccinationTypeTest::testWhenMissingBrandThenViolation Symfony\Component\Form\Exception\RuntimeException: Class "App\Entity\ViewVaccineBrand" 似乎不是托管的 Doctrine 实体。 Did you forget to map it?你忘了map吗?

/app/xxxxapi/vendor/symfony/doctrine-bridge/Form/Type/DoctrineType.php:203 /app/xxxxapi/vendor/symfony/doctrine-bridge/Form/Type/DoctrineType.php:203 /app/xxxxapi/vendor/symfony/options-resolver/OptionsResolver.php:1035 /app/xxxxapi/vendor/symfony/doctrine-bridge/Form/Type/DoctrineType.php:130 /app/xxxxapi/vendor/symfony/options-resolver/OptionsResolver.php:915 /app/xxxxapi/vendor/symfony/options-resolver/OptionsResolver.php:824 /app/xxxxapi/vendor/symfony/form/ResolvedFormType.php:97 /app/xxxxapi/vendor/symfony/form/FormFactory.php:76 /app/xxxxapi/vendor/symfony/form/FormBuilder.php:94 /app/xxxxapi/vendor/symfony/form/FormBuilder.php:244 /app/xxxxapi/vendor/symfony/form/FormBuilder.php:195 /app/xxxxapi/vendor/symfony/form/FormFactory.php:30 /app/xxxxapi/tests/Service/Domain/Vaccination/Type/VaccinationTypeTest.php:23 /app/xxxxapi/vendor/symfony/doctrine-bridge/Form/Type/DoctrineType.php:203 /app/xxxxapi/vendor/symfony/doctrine-bridge/Form/Type/DoctrineType.php:203 /app/xxxxapi/vendor /symfony/options-resolver/OptionsResolver.php:1035 /app/xxxxapi/vendor/symfony/doctrine-bridge/Form/Type/DoctrineType.php:130 /app/xxxxapi/vendor/symfony/options-resolver/OptionsResolver.88145283851953 :915 /app/xxxxapi/vendor/symfony/options-resolver/OptionsResolver.php:824 /app/xxxxapi/vendor/symfony/form/ResolvedFormType.php:97 /app/xxxxapi/vendor/symfony/form/FormFactory.881452835199 :76 /app/xxxxapi/vendor/symfony/form/FormBuilder.php:94 /app/xxxxapi/vendor/symfony/form/FormBuilder.php:244 /app/xxxxapi/vendor/symfony/form/FormBuilder.php:195 /app/xxxxapi/vendor/symfony/form/FormFactory.php:30 /app/xxxxapi/tests/Service/Domain/Vaccination/Type/VaccinationTypeTest.php:23

I think that's because for some reason, the entitymanager created in the EntityTypeTestCase is not the same as the one used at /app/xxxxx/vendor/symfony/doctrine-bridge/Form/Type/DoctrineType.php:203 ...我认为这是因为出于某种原因,在 EntityTypeTestCase 中创建的 entitymanager 与 /app/xxxxx/vendor/symfony/doctrine-bridge/Form/Type/DoctrineType.php:203 中使用的/app/xxxxx/vendor/symfony/doctrine-bridge/Form/Type/DoctrineType.php:203 ......

But I don't know how to specify that, in the case of this test, I want that DoctrineType (which is parent of EntityType ) use this special test entityManager instead of the default one.但我不知道如何指定,在这个测试的情况下,我希望DoctrineType (它是EntityType的父级)使用这个特殊的测试 entityManager 而不是默认的。

Expected result预期结果

Make the test work an the assertion should be successfull.使测试工作断言应该是成功的。

Edit编辑

I add the Entity for extra informations我添加实体以获取额外信息


namespace App\Entity;

/**
 * VaccineBrand
 *
 * @ORM\Table(name="dbo.V_HAP_VACCINE_BRAND")
 * @ORM\Entity(repositoryClass="App\Repository\ViewVaccineBrandRepository")
 */
class ViewVaccineBrand
{
    /**
     * @var int
     *
     * @ORM\Column(name="VACCINE_BRAND_UID", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $vaccineBrandUid;

    /**
     * @var string
     * @ORM\Column(name="VACCINE_BRAND_NAME", type="string", nullable=false)
     */
    protected $vaccineBrandName;

    /**
     * @var string
     * @ORM\Column(name="VACCINE_BRAND_CODE", type="string", nullable=true)
     */
    protected $vaccineBrandCode;
// ... Other fields are not relevant
}

PS聚苯乙烯

I already read those one with no luck:我已经读过那些没有运气的人:

This is a complicated situation, so I will provide an abbreviated outline of what I believe you need to do .这是一个复杂的情况,因此我将简要概述我认为您需要做的事情

  • Change EntityType (for related entity) to ChoiceTypeEntityType (对于相关实体)更改为ChoiceType
    • Use a CallbackChoiceLoader that queries the DB for all the uid fields indexed by name (or whatever you want to be the label), ie: ['BigPharm'=>123, 'Other Brand'=>564, ] 使用CallbackChoiceLoader在数据库中查询所有按名称(或任何你想成为标签)索引的 uid 字段,即: ['BigPharm'=>123, 'Other Brand'=>564, ]
  • Change @Assert\Type to @Assert\Choice@Assert\Type更改为@Assert\Choice
    • Use the callback option with a method that will call array_values on the result of the same as the CallbackChoiceLoader above, ie: [123, 564, ]callback选项与将在与上述CallbackChoiceLoader相同的结果上调用array_values的方法一起使用,即: [123, 564, ]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM