Php 使用实体对Symfony表单进行单元测试
我很难对内部有“实体”字段的Symfony表单进行单元测试 我找到了潜在的解决方案,但我无法让它们发挥作用 这是我的密码: FormsTest.phpPhp 使用实体对Symfony表单进行单元测试,php,forms,unit-testing,symfony,Php,Forms,Unit Testing,Symfony,我很难对内部有“实体”字段的Symfony表单进行单元测试 我找到了潜在的解决方案,但我无法让它们发挥作用 这是我的密码: FormsTest.php protected function setUp() { parent::setUp(); $this->factory = Forms::createFormFactoryBuilder() ->addExtensions($this->getExtensions()) -&g
protected function setUp()
{
parent::setUp();
$this->factory = Forms::createFormFactoryBuilder()
->addExtensions($this->getExtensions())
->getFormFactory();
}
protected function getExtensions()
{
$mockEntityType = $this->getMockBuilder('Symfony\Bridge\Doctrine\Form\Type\EntityType')
->disableOriginalConstructor()
->getMock();
$mockEntityType->expects($this->any())->method('getName')
->will($this->returnValue('entity'));
return array(new PreloadedExtension(array(
$mockEntityType->getName() => $mockEntityType,
), array()));
}
public function testSubmitValidData()
{
$formData = array(
'name' => 'Mbalmayo',
'latitude' => 3.5165475,
'longitude' => 11.5144015,
'zoomLevel' => 12.0,
'region' => 'Centre',
);
$type = new CitiesType();
$form = $this->factory->create($type, null);
$object = new Cities();
$object->fromArray($formData);
// submit the data to the form directly
$form->submit($formData);
$this->assertTrue($form->isSynchronized());
$this->assertEquals($object, $form->getData());
$view = $form->createView();
$children = $view->children;
foreach (array_keys($formData) as $key) {
$this->assertArrayHasKey($key, $children);
}
}
/**
* Builds the form data for the cities
*
* @param FormBuilderInterface $builder The FormBuilderInterface to use
* @param array $options The options for the form, if any
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
/* Several adds that are pointless for this problem */
->add('region', 'entity', array('class' => 'SmopaAgentFinderBundle:Regions',
'property' => 'name',
'required' => true,
'label' => 'city.new.region',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('r')
->orderBy('r.name', 'ASC');
},
'empty_value' => 'Select city\'s region',
'attr' => array('class' => 'new_city_combo_box')
)
);
}
此代码基于我以前找到的解决方案
CitiesType.php
protected function setUp()
{
parent::setUp();
$this->factory = Forms::createFormFactoryBuilder()
->addExtensions($this->getExtensions())
->getFormFactory();
}
protected function getExtensions()
{
$mockEntityType = $this->getMockBuilder('Symfony\Bridge\Doctrine\Form\Type\EntityType')
->disableOriginalConstructor()
->getMock();
$mockEntityType->expects($this->any())->method('getName')
->will($this->returnValue('entity'));
return array(new PreloadedExtension(array(
$mockEntityType->getName() => $mockEntityType,
), array()));
}
public function testSubmitValidData()
{
$formData = array(
'name' => 'Mbalmayo',
'latitude' => 3.5165475,
'longitude' => 11.5144015,
'zoomLevel' => 12.0,
'region' => 'Centre',
);
$type = new CitiesType();
$form = $this->factory->create($type, null);
$object = new Cities();
$object->fromArray($formData);
// submit the data to the form directly
$form->submit($formData);
$this->assertTrue($form->isSynchronized());
$this->assertEquals($object, $form->getData());
$view = $form->createView();
$children = $view->children;
foreach (array_keys($formData) as $key) {
$this->assertArrayHasKey($key, $children);
}
}
/**
* Builds the form data for the cities
*
* @param FormBuilderInterface $builder The FormBuilderInterface to use
* @param array $options The options for the form, if any
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
/* Several adds that are pointless for this problem */
->add('region', 'entity', array('class' => 'SmopaAgentFinderBundle:Regions',
'property' => 'name',
'required' => true,
'label' => 'city.new.region',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('r')
->orderBy('r.name', 'ASC');
},
'empty_value' => 'Select city\'s region',
'attr' => array('class' => 'new_city_combo_box')
)
);
}
目前,我遇到了以下错误:
1) FormsTest::testSubmitValidData
Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException:
选项“属性”、“类”、“空值”、“标签”、“属性”,
“查询生成器”、“必需”不存在。已知选项为:“”
我需要用测试覆盖这些表单,我完全没有想法。有任何帮助吗?从symfony 2.7开始,“属性”选项已被“选择标签”取代
发生这种情况是因为选项解析器不知道这些选项。EntityType模拟应声明它们: 对于Symfony 2.7:
//使用Symfony\Component\OptionsResolver\OptionsResolver;
$mockEntityType->method('setDefaultOptions')->将(
$this->returnCallback(
功能(选项解析器$resolver)
{
$resolver->setDefaults(
排列(
“选择标签”=>null,
'class'=>null,
“查询生成器”=>null,
“必需”=>null,
)
);
}
)
);
我想介绍一下我的解决方案。首先,我将实体类型注入到表单中。以下是我在UserBundle中所做的操作,它扩展了FOSUserBundle,因为我需要将组分配给用户:
namespace UserBundle\Form\Type;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Email;
use UserBundle\Entity\Group;
/**
* Class UserType
* @package UserBundle\Form\Type
*/
class UserType extends AbstractType
{
/**
* @var array
*/
private $roles;
/**
* GroupType constructor.
* @param $roles
*/
public function __construct($roles)
{
$this->roles = $roles;
}
/**
* @var EntityType
*/
private $entityType;
/**
* @param EntityType $entityType
* @internal param $imagepath
*/
public function setEntityType(
$entityType
)
{
$this->entityType = $entityType;
}
/**
* Mainly for testing
* @return bool
*/
public function hasEntityType(){
return $this->entityType instanceof EntityType;
}
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('enabled',
CheckboxType::class,
[
'label' => 'smartadmin.user.active',
'required' => false
]
)->add('groups',
$this->entityType,
[
'class' => Group::class,
'property' => 'name',
'multiple' => true,
'expanded' => true,
'required' => false,
'label' => 'smartadmin.user.groups'
]
)->add('username',
TextType::class,
[
'label' => 'form.username',
'translation_domain' => 'FOSUserBundle'
]
)->add('email',
EmailType::class,
[
'label' => 'form.email',
'translation_domain' => 'FOSUserBundle',
'constraints' => [new Email()]
]
)->add('firstname',
TextType::class,
[
'label' => 'smartadmin.user.firstname'
]
)->add('lastname',
TextType::class,
[
'label' => 'smartadmin.user.lastname'
]
)->add('gender',
ChoiceType::class,
[
'label' => 'smartadmin.user.gender',
'choices' => [1 => 'smartadmin.user.gender_male', 2 => 'smartadmin.user.gender_female']
]
)->add('plainPassword', RepeatedType::class,
[
'type' => PasswordType::class,
'options' => array('translation_domain' => 'FOSUserBundle'),
'first_options' => array('label' => 'form.new_password'),
'second_options' => array('label' => 'form.new_password_confirmation'),
'invalid_message' => 'fos_user.password.mismatch',
'required' => false
]
);
}
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'UserBundle\Entity\User',
'csrf_token_id' => 'profile'
));
}
}
在我的控制器中:
$oUser = $this->userRepository->find($userId);
$userType = new UserType($this->roleService->getAvailableRoles());
$userType->setEntityType(new EntityType($this->doctrine));
$oForm = $this->formFactory->createBuilder($userType)
->setData($oUser)
->getForm();
为了测试此表单,我创建了一个替换实体类型,该类型仅提供与entityType类似的必需方法:
namespace UserBundle\Tests\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\Util\StringUtil;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Class EntityTypeSimulator
* @package UserBundle\Tests\Form\Type
*/
class EntityTypeSimulator extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
}
/**
* {@inheritdoc}
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
}
/**
* {@inheritdoc}
*/
public function finishView(FormView $view, FormInterface $form, array $options)
{
}
/**
* {@inheritdoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
if (!$resolver instanceof OptionsResolver) {
throw new \InvalidArgumentException(sprintf('Custom resolver "%s" must extend "Symfony\Component\OptionsResolver\OptionsResolver".', get_class($resolver)));
}
$this->configureOptions($resolver);
}
/**
* Configures the options for this type.
*
* @param OptionsResolver $resolver The resolver for the options.
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'multiple' => false,
'expanded' => false,
'class' => null,
'property' => true,
'invalid_message' => null
));
}
/**
* {@inheritdoc}
*/
public function getName()
{
// As of Symfony 2.8, the name defaults to the fully-qualified class name
return get_class($this);
}
/**
* Returns the prefix of the template block name for this type.
*
* The block prefixes default to the underscored short class name with
* the "Type" suffix removed (e.g. "UserProfileType" => "user_profile").
*
* @return string The prefix of the template block name
*/
public function getBlockPrefix()
{
$fqcn = get_class($this);
$name = $this->getName();
// For BC: Use the name as block prefix if one is set
return $name !== $fqcn ? $name : StringUtil::fqcnToBlockPrefix($fqcn);
}
/**
* {@inheritdoc}
*/
public function getParent()
{
return 'Symfony\Component\Form\Extension\Core\Type\FormType';
}
}
现在是我的测试用例:
namespace UserBundle\Tests\Form\Type;
use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
use Symfony\Component\Form\Test\TypeTestCase;
use Symfony\Component\Validator\Validation;
use UserBundle\Entity\User;
use UserBundle\Form\Type\UserType;
/**
* Class UserTypeTest
* @package UserBundle\Tests\Form\Type
*/
class UserTypeTest extends TypeTestCase
{
/**
* Load the ValidatorExtension so RepeatedType can resolve 'invalid_message'
* @return array
*/
protected function getExtensions()
{
return array(new ValidatorExtension(Validation::createValidator()));
}
public function testGroupType(){
$formData['user'] = [
'enabled' => 1,
'groups' => [1,2,3],
'username' => 'Test username',
'email' => 'test@email.de',
'firstname' => 'Test Firstname',
'lastname' => 'Test Lastname',
'gender' => 1,
'plainPassword' => ['first' => 'Test Password', 'second' => 'Test Password']
];
$roles = ['ROLE_USER'];
$entity = new User('Test name');
$type = new UserType($roles);
$type->setEntityType(new EntityTypeSimulator());
$oFormBuilder = $this->factory->createBuilder();
$oFormBuilder->add('user', $type);
$oFormBuilder->setData(['user' => $entity]);
$oForm = $oFormBuilder->getForm();
$oForm->submit($formData);
$oForm->handleRequest();
$this->assertTrue($oForm->isSynchronized());
$view = $oForm->createView();
$children = $view->children['user']->children;
foreach (array_keys($formData['user']) as $key) {
$this->assertArrayHasKey($key, $children);
}
$this->assertEquals($formData['user']['username'], $entity->getUsername());
$this->assertEquals($formData['user']['email'], $entity->getEmail());
}
}
错误来自表单声明,而不是您的测试。请参阅,选项
attr
,空值
等不适用于此类字段。但是class
可用,因此这可能不是原因。请查看此问题:。我认为EntityType应该是开箱即用的可测试的。通过此设置,我得到了一个TypeError:参数1传递给Symfony\Bridge\Doctrine\Form\Type\DoctrineType::\uu construct()必须实现接口原则\Common\Persistence\manager注册表,未给出,使用symfony 3.0.6在第85行调用/var/www/shn/vendor/symfony/symfony/src/symfony/Component/Form/FormRegistry.php。当然,我将setDefaultOptions更改为configureOptions