Unit Testing Symfony Forms with Objects

I am having problems with unit test Symfony forms that have an "entity" field inside.

I found potential solutions here and here , however I couldn't get them to work.

Here's my code:

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);
    }
}

      

This code is based on previous solutions found.

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\ region',
                'attr' => array('class' => 'new_city_combo_box')
            )
        );
}

      

I am currently getting this error:

1) FormsTest :: testSubmitValidData Symfony \ Component \ OptionsResolver \ Exception \ UndefinedOptionsException: The parameters "attr", "class", "empty_value", "label", "property", "query_builder", "required" do not exist. The following options are known: "".

I need these forms to be embraced by trials and I'm completely out of ideas. Any help?

+3


source to share


3 answers


This is because the OptionsResolver does not know these parameters. Your EntityType mock should declare them:

For 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,
                )
            );
        }
    )
);

      

+2


source


Option

'property' has been replaced with 'choice_label' from Symfony 2.7 onwards.



documentation

0


source


I would like to present my solution. First, I injected the entity type into my form. Here's what I did in my UserBundle, which extends FOSUserBundle, because I needed to assign groups to users:

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'
        ));
    }
}

      

And in my controller:

$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();

      

To test this form, I created a replacement entity type that provides only the necessary methods to act as 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';
    }
}

      

And now my test case:

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());
    }

}

      

0


source







All Articles