[英]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;
}
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;
}
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),
]);
}
}
The actual result of the php bin/phpunit
execution is as follow: php bin/phpunit
执行的实际结果如下:
There was 1 error:
有 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 而不是默认的。
Make the test work an the assertion should be successfull.使测试工作断言应该是成功的。
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
}
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 .这是一个复杂的情况,因此我将简要概述我认为您需要做的事情。
EntityType
(for related entity) to ChoiceType
EntityType
(对于相关实体)更改为ChoiceType
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, ]
@Assert\Type
to @Assert\Choice
@Assert\Type
更改为@Assert\Choice
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.