【发布时间】:2017-02-20 06:19:48
【问题描述】:
注意:这是 Symfony
首先,请考虑这种将一个或多个实体表示为隐藏字段的表单类型(为简洁起见,省略了命名空间的内容)
class HiddenEntityType extends AbstractType
{
/**
* @var EntityManager
*/
protected $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
if ($options['multiple']) {
$builder->addViewTransformer(
new EntitiesToPrimaryKeysTransformer(
$this->em->getRepository($options['class']),
$options['get_pk_callback'],
$options['identifier']
)
);
} else {
$builder->addViewTransformer(
new EntityToPrimaryKeyTransformer(
$this->em->getRepository($options['class']),
$options['get_pk_callback']
)
);
}
}
/**
* See class docblock for description of options
*
* {@inheritdoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'get_pk_callback' => function($entity) {
return $entity->getId();
},
'multiple' => false,
'identifier' => 'id',
'data_class' => null,
));
$resolver->setRequired(array('class'));
}
public function getName()
{
return 'hidden_entity';
}
/**
* {@inheritdoc}
*/
public function getParent()
{
return 'hidden';
}
}
这很有效,很简单,而且大部分看起来就像您看到的所有将数据转换器添加到表单类型的示例。直到你进行单元测试。看到问题了吗?变压器不能被嘲笑。 “可是等等!”你说,“Symfony 表单的单元测试是集成测试,它们应该确保转换器不会失败。即使这样说 in the documentation!”
此测试检查表单未使用任何数据转换器 失败的。 isSynchronized() 方法仅在有数据时设置为 false 变压器抛出异常
好的,那么您可以接受无法隔离变压器的事实。没什么大不了?
现在考虑在对具有此类型字段的表单进行单元测试时会发生什么(假设 HiddenEntityType 已在服务容器中定义和标记)
class SomeOtherFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('field', 'hidden_entity', array(
'class' => 'AppBundle:EntityName',
'multiple' => true,
));
}
/* ... */
}
现在进入问题。 SomeOtherFormType 的单元测试现在需要实现 getExtensions() 才能使 hidden_entity 类型起作用。那看起来怎么样?
protected function getExtensions()
{
$mockEntityManager = $this
->getMockBuilder('Doctrine\ORM\EntityManager')
->disableOriginalConstructor()
->getMock();
/* Expectations go here */
return array(
new PreloadedExtension(
array('hidden_entity' => new HiddenEntityType($mockEntityManager)),
array()
)
);
}
看到评论在中间的什么位置了吗?是的,因此为了使其正常工作,HiddenEntityType 的单元测试类中的所有模拟和期望现在都需要在此处有效地复制。我对此不满意,那我有什么选择?
-
注入变压器作为选项之一
这将非常简单,并且会使模拟变得更简单,但最终只会把罐子踢到路上。因为在这种情况下,
new EntityToPrimaryKeyTransformer()只会从一种表单类型类移动到另一种。更不用说我觉得表单类型应该对系统的其他部分隐藏它们的内部复杂性。此选项意味着将复杂性推到表单类型的边界之外。 -
在表单类型中注入各种变压器工厂
这是一种更典型的从方法中删除“newables”的方法,但我无法摆脱这样的感觉,即这样做只是为了使代码可测试,而实际上并没有使代码变得更好。但是如果这样做了,它会看起来像这样
class HiddenEntityType extends AbstractType { /** * @var DataTransformerFactory */ protected $transformerFactory; public function __construct(DataTransformerFactory $transformerFactory) { $this->transformerFactory = $transformerFactory; } public function buildForm(FormBuilderInterface $builder, array $options) { $builder->addViewTransformer( $this->transformerFactory->createTransfomerForType($this, $options); ); } /* Rest of type unchanged */ }在我考虑工厂的实际外观之前,这感觉还不错。首先,它需要注入实体管理器。但那又如何呢?如果我往前看,这个所谓的通用工厂可能需要各种依赖项来创建不同类型的数据转换器。这显然不是一个好的长期设计决策。那么呢?将其重新标记为
EntityManagerAwareDataTransformerFactory?这里开始有点乱了。 我没想到的东西...
想法?经验?中肯的建议?
【问题讨论】:
标签: php unit-testing symfony symfony-forms