(使用框架Symfony 4.4)
我试着学习如何测试一个表单有一个字段是一个EntityType表单字段。
请看下面的例子:
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
]);
}
}
您可以看到,字段vaccine_brand_uid是一个EntityType字段,因此它确保提交表单时给定的值是ViewVaccineBrand
表的一部分。
下面是相关的VaccinationInput
对象:
namespace AppServicesDomainVaccinationDTO;
use AppEntityViewVaccineBrand;
...
class VaccinationInput
{
/**
* @AssertType(type=ViewVaccineBrand::class, message="api_missingBrand")
* @AssertNotBlank(message="api_missingBrand")
* @var ViewVaccineBrand|int
*/
public $vaccine_brand_uid;
/**
* @AssertDateTime(message="api_missingAdministrationDate")
*/
public $administration_date;
}
为测试带有EntityType字段的表单创建一个基类
因此,在尝试为这个表单创建一个测试类时,我在Symfony存储库中发现了这个例子:https://github.com/symfony/symfony/blob/4.4/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php
所以我复制/粘贴这个类来适应我自己的测试!
我对它进行了一些调整,使其更易于重用(添加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;
}
为我的疫苗接种表单创建一个特定的测试类
所以我想测试我的VaccinationType表单,下面是我所做的。
~/symfony/tests/Service/Domain/Vaccination/Type/VaccinationTypeTest
<?php
namespace AppTestsServiceDomainVaccinationType;
...
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),
]);
}
}
实际结果php bin/phpunit
执行的实际结果如下:
有1个错误:
- App 测试服务域接种 VaccinationTypeTest类型::testWhenMissingBrandThenViolationSymfonyComponentFormExceptionRuntimeException: Class "AppEntityViewVaccineBrand"似乎不是一个受管理的Doctrine实体。你忘了画地图吗?
/app/xxxxapi/供应商/symfony/doctrine-bridge/形式/类型/DoctrineType.php: 203/应用程序/xxxxapi/供应商/symfony/doctrine-bridge/形式/类型/DoctrineType.php: 203/app/xxxxapi/供应商/symfony/options-resolver/OptionsResolver.php: 1035/应用程序/xxxxapi/供应商/symfony/doctrine-bridge/形式/类型/DoctrineType.php: 130/app/xxxxapi/供应商/symfony/options-resolver/OptionsResolver.php: 915/app/xxxxapi/供应商/symfony/options-resolver/OptionsResolver.php: 824/app/xxxxapi/供应商/symfony/形式/ResolvedFormType.php: 97/app/xxxxapi/供应商/symfony/形式/FormFactory.php: 76/app/xxxxapi/供应商/symfony/形式/FormBuilder.php: 94/app/xxxxapi/供应商/symfony/形式/FormBuilder.php: 244/app/xxxxapi/供应商/symfony/形式/FormBuilder.php: 195/app/xxxxapi/供应商/symfony/形式/FormFactory.php: 30/app/xxxxapi/测试/服务/域/接种/类型/VaccinationTypeTest.php: 23日
我认为这是因为出于某种原因,在EntityTypeTestCase中创建的entitymanager与/app/xxxxx/vendor/symfony/doctrine-bridge/Form/Type/DoctrineType.php:203
中使用的entitymanager不一样…
但是我不知道如何指定,在这个测试的情况下,我希望DoctrineType
(这是EntityType
的父)使用这个特殊的测试entityManager而不是默认的。
预期结果让测试工作,断言应该是成功的。
编辑
我添加了额外信息的实体
namespace AppEntity;
/**
* VaccineBrand
*
* @ORMTable(name="dbo.V_HAP_VACCINE_BRAND")
* @ORMEntity(repositoryClass="AppRepositoryViewVaccineBrandRepository")
*/
class ViewVaccineBrand
{
/**
* @var int
*
* @ORMColumn(name="VACCINE_BRAND_UID", type="integer")
* @ORMId
* @ORMGeneratedValue(strategy="AUTO")
*/
protected $vaccineBrandUid;
/**
* @var string
* @ORMColumn(name="VACCINE_BRAND_NAME", type="string", nullable=false)
*/
protected $vaccineBrandName;
/**
* @var string
* @ORMColumn(name="VACCINE_BRAND_CODE", type="string", nullable=true)
*/
protected $vaccineBrandCode;
// ... Other fields are not relevant
}
p
我已经读过那些没有运气的了:
- = =比;https://stackoverflow.com/a/49879186/6503197这一个正是我所尝试的,并不奏效。你也可以从答案中看到没有经过验证的解决方案。
- = =比;https://symfony.com/doc/current/form/unit_testing.html#testing-types-registered-as-services官方文档并没有真正精确地说明如何在这种情况下做事情,尽管它提到了DoctrineOrmExtension,但没有解释更多。
这是一个复杂的情况,所以我将提供一个简短的概要我认为你需要做的
- 修改
EntityType
(对应实体)为ChoiceType
- 使用
CallbackChoiceLoader
查询DB的所有uid字段索引的名称(或任何你想成为标签),即:['BigPharm'=>123, 'Other Brand'=>564, ]
- 使用
- 将
@AssertType
更改为@AssertChoice
- 使用
callback
选项,该方法将调用array_values
的结果与上面的CallbackChoiceLoader
相同,即:[123, 564, ]
- 使用