Symfony2 Unit testing form submission
forms, php, symfony, unit-testing
Solution
And then people go on to whine that TDD is stupid, it's hard, it sucks, and, finally, it's dead...
Do yourself a favor and stop trying to unit test stuff you should not be unit testing in the first place. Write some higher level test. You have at least two options.
First, you could write integration tests specific to that form that test its behavior in the wider context. For example, you could create an instance of the form, pass data to it and then test that the model that you get out of the form has the data you passed.
Second, you could write end-to-end tests that involve this form. If the form doesn't work right, the tests will fail.
Since end-to-end tests are usually slower, you could actually write one or two of them to test that your code behaves correctly when the form is both valid and not valid. For instance, when the form is valid, a model is persisted and the user is redirected somewhere. If it's not, nothing gets persisted and the form is shown again with previously entered data and validation messages.
And then you could write several integration tests specific to the form to test some other combinations of data input to make sure it behaves correctly in those cases.
Actually, if you're testing validation, you could omit integration tests for the form itself and write tests to test validation rules right on the model. But you'd still need end-to-end tests to test the overall flow of the app.
Problem
I have a form which looks like this, when it is rendered: ``` <form name="service_user_registration" method="post" action="" novalidate="novalidate"> <div> <label for="service_user_registration_user_email" class="required">E-mail</label> <input type="email" id="service_user_registration_user_email" name="service_user_registration[user][email]" required="required" size="30" class="sample_class" title="Sample title" placeholder="Your e-mail..." /> </div> <div> <label for="service_user_registration_account_name" class="required">Company name</label> <input type="text" id="service_user_registration_account_name" name="service_user_registration[account][name]" required="required" placeholder="Your company name..." /> </div> <button type="submit" id="service_user_registration_register" name="service_user_registration[register]" class="btn btn-primary">Register</button> <input type="hidden" id="service_user_registration__token" name="service_user_registration[_token]" value="VzkWJQw-2p-1lfBJoT5xwNB4dR2reT47i0ZTZES9LqY" /> </form> ``` I want to unit test the form submission, if the data that I enter is written in the underlying object, which is a Registration object. I do it like below, but I do not know how to set the $data array properly, because name attribute of fields appears to be like an array. ``` class RegistrationTypeTest extends TypeTestCase { public function testSubmit() { $user = new User(); $account = new Account(); $registration = new Registration($user,$account); $form = $this->factory->create(new RegistrationType(), $registration); $data = array( 'service_user_registration[user][email]' => 'testing@test.com', 'service_user_registration[account][name]' => 'New company' ); $form->submit($data); $this->assertTrue($form->isSynchronized()); $formData = $form->getData(); $this->assertInstanceOf('Service\Bundle\UserBundle\Entity\Registration', $formData); $this->assertEquals($data['service_user_registration[user][email]'], $formData->getUser()->getEmail()); $this->assertEquals($data['service_user_registration[account][name]'], $formData->getAccount()->getName()); } } ``` The last two asserts fail because the $formData in a Registration object, has everything null, which should be the case when the submitted fields are both blank. I have also written the $data like an array of arrays, but did not change anything. Edit (added form types): ``` class RegistrationType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('user', new RegistrationUserType()) ->add('account', new RegistrationAccountType()) ->add('register', 'submit'); } public function setDefaultOptions(OptionsResolverInterface $resolver) { # Using cascade validation because Valid constraint can not have validation groups $resolver->setDefaults([ 'cascade_validation' => true, 'intention' => $this->getName() ]); } public function getName() { return 'service_user_registration'; } } class RegistrationUserType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('email', 'email'); } public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( 'validation_groups' => array('registration'), 'data_class' => 'Service\Bundle\UserBundle\Entity\User', 'intention' => $this->getName() )); } public function getName() { return 'service_user_user_registration'; } } class RegistrationAccountType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('name', 'text'); } public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( 'validation_groups' => array('registration'), 'data_class' => 'Service\Bundle\UserBundle\Entity\Account', 'intention' => $this->getName() )); } public function getName() { return 'service_user_account_registration'; } } ```