我很难对内部有"entity"字段的Symfony表单进行单元测试。
我在这里和这里找到了潜在的解决方案,但我无法使它们发挥作用。
这是我的代码:
FormsTest.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);
}
}
此代码基于我以前找到的解决方案。
CitiesType.php
/**
* 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::testSubmitValidDataSymfony''Component''选项解算器''异常''未定义选项异常:选项"attr"、"class"、"empty_value"、,"query_builder"、"required"不存在。已知的选项有:"。
我需要用测试来覆盖这些表格,我完全没有想法了。有什么帮助吗?
之所以发生这种情况,是因为OptionsResolver不知道这些选项。EntityType mock应该声明它们:
对于Symfony 2.7:
// use Symfony'Component'OptionsResolver'OptionsResolver;
$mockEntityType->method('setDefaultOptions')->will(
$this->returnCallback(
function (OptionsResolver $resolver)
{
$resolver->setDefaults(
array(
'choice_label' => null,
'class' => null,
'query_builder' => null,
'required' => null,
)
);
}
)
);
从symfony 2.7起,'property'选项已被'choice_label'取代。
文档
我想介绍一下我的解决方案。首先,我将实体类型注入到我的表单中。以下是我在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();
为了测试这个表单,我创建了一个替换实体类型,它只提供了像实体类型一样工作所需的方法:
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());
}
}