Change form action with symfony2 and phpunit

I am currently working with Symfony2, and I am testing my project using PHPUnit. I want to check for an exception when the form is submitted with the wrong parameters or the URL is not completed.

I went through the Symfony2 and PHPUnit documentation, but did not find any class / method for this.

How to change the value of a form action? I want to use PHPUnit, so the generated report is updated, and I see the coverage of my code.

EDIT:

To clarify my question, new content has appeared. How to check a line starting with '>' in my controller? ( throw $this->createNotFoundException('Unable to find ParkingZone entity.'); )

When a user changes the link to an action, in the controller the process will go through an exception (or an error message if this action is selected). How can I check this case?

controller

 /** * Edits an existing ParkingZone entity. * * @Route("/{id}/update", name="parkingzone_update") * @Method("post") * @Template("RatpGarageL1Bundle:ParkingZone:edit.html.twig") */ public function updateAction($id) { $em = $this->getDoctrine()->getEntityManager(); $entity = $em->getRepository('RatpGarageL1Bundle:ParkingZone')->find($id); if (!$entity) { > throw $this->createNotFoundException('Unable to find ParkingZone entity.'); } $editForm = $this->createForm(new ParkingZoneType(), $entity); $deleteForm = $this->createDeleteForm($id); $request = $this->getRequest(); $editForm->bindRequest($request); if ($editForm->isValid()) { $em->persist($entity); $em->flush(); return $this->redirect($this->generateUrl('parkingzone_edit', array('id' => $id))); } return array( 'entity' => $entity, 'edit_form' => $editForm->createView(), 'delete_form' => $deleteForm->createView(), ); } 

View:

 <form action="{{ path('parkingzone_update', { 'id': entity.id }) }}" method="post" {{ form_enctype(form) }}> <div class="control-group"> {{ form_label(form.name, 'Name', { 'attr': {'class': 'control-label'} } ) }} <div class="controls error"> {{ form_widget(form.name, { 'attr': {'class': ''} } ) }} <span class="help-inline">{{ form_errors(form.name) }}</span> </div> </div> <div class="control-group"> {{ form_label(form.orderSequence, 'Rang', { 'attr': {'class': 'control-label'} } ) }} <div class="controls error"> {{ form_widget(form.orderSequence, { 'attr': {'class': ''} } ) }} <span class="help-inline">{{ form_errors(form.orderSequence) }}</span> </div> </div> <div class="control-group"> {{ form_label(form.image, 'Image', { 'attr': {'class': 'control-label'} } ) }} <div class="controls error"> {{ form_widget(form.image, { 'attr': {'class': ''} } ) }} <span class="help-inline">{{ form_errors(form.image) }}</span> </div> </div> {{ form_rest(form) }} <div class="form-actions"> <button type="submit" class="btn btn-primary">Enregistrer</button> <a href="{{ path('parkingzone') }}" class="btn">Annuler</a> </div> </form> 
+4
source share
3 answers

Symfony itself has no objects through which it is possible to manipulate the action of the form, as it is set in html (twig files). However, the branch provides the ability to dynamically change the action of the form in the twig file.

The basic approach is for the controller to pass the parameter to the twig file through a render call. The twig file can then use this option to dynamically change the form. If the controller uses a session variable to determine the value of this parameter, then by setting the value of this session variable to the test program, you can set the form action specifically for the test.

For example, in the controller:

 public function indexAction() { $session = $this->get('session'); $formAction = $session->get('formAction'); if (empty($formAction)) $formAction = '/my/normal/route'; ... return $this->render('MyBundle:XXX:index.html.twig', array( 'form' => $form->createView(), 'formAction' => $formAction) ); } 

And then in the twig file:

 <form id="myForm" name="myForm" action="{{ formAction }}" method="post"> ... </form> 

And then in the test program:

 $client = static::createClient(); $session = $client->getContainer()->get('session'); $session->set('formAction', '/test/route'); $session->save(); // do the test 

This is not the only way, there are various possibilities. For example, a session variable might be $ testMode, and if this variable is set, the form passes $ testMode = true to the render call. Then the twig file can set the form action to one of two values, depending on the value of the testMode variable.

+2
source

Symfony2 distinguishes between unit testing of individual classes and functional testing of application behavior. Unit testing is done by directly creating a class and calling methods on it. Functional testing is done by simulating requests and testing responses. See symfony testing for more details.

Form submission can only be checked functionally, because it is processed by the Symfony controller, which always works in the context of the container. Symfony function tests should extend the WebTestCase class. This class provides access to the client, which is used to request URLs, click on links, select buttons and submit forms. These actions return a crawler instance representing the HTML response, which is used to verify that the response contains the expected content.

It is only relevant to verify that exceptions are performed during unit tests, as functional tests cover user interaction. The user should never know that an exception has been thrown. Therefore, the worst case scenario is that the exception is thrown into Symfony, and during the production process the user is given the answer "Full answer", "Error, an error occurred" (or a similar configured message). However, this should only happen when the application is broken, and not because the user misused the application. Therefore, this is not what was usually tested in a functional test.

Regarding the first scenario mentioned in the question - submitting the form with the wrong parameters. In this case, the user should be presented with an error message stating what was wrong with their input. Ideally, the controller should not throw an exception, but the symfony check should be used to automatically generate error messages next to each field, if necessary. Regardless of how errors are displayed, this can be checked by checking that the response to the form submission contains the expected errors. For instance:

 class MyControllerTest extends WebTestCase { public function testCreateUserValidation() { $client = static::createClient(); $crawler = $client->request('GET', '/new-user'); $form = $crawler->selectButton('submit')->form(); $crawler = $client->submit($form, array('name' => '', 'email' => 'xxx')); $this->assertTrue($crawler->filter('html:contains("Name must not be blank")')->count() > 0, "Page contains 'Name must not be blank'"); $this->assertTrue($crawler->filter('html:contains("Invalid email address")')->count() > 0, "Page contains 'Invalid email address'"); } } 

As for the second scenario - the URL is not populated. With Symfony, any URL that does not match a specific route will throw a NotFoundHttpException. During development, this will result in a message such as “No route found for“ GET / xxx. ”During production, this will catch“ Oops, an error occurred. ”During development, you could check that the response contains“ No route found ” However, in practice, it does not make sense to test this, since it is processed by the Symfony framework and therefore is data.

EDIT:

Regarding a scenario in which the URL contains invalid data that identifies the object. This can be tested (in development), as in the unit test program:

 $client = static::createClient(); $page = $client->request('GET', '/update/XXX'); $exceptionThrown = ($page->filter('html:contains("NotFoundException")')->count() > 0) && ($page->filter('html:contains("Unable to find ParkingZone entity")')->count() > 0); $this->assertTrue($exceptionThrown, "Exception thrown 'Unable to find ParkingZone entity'"); 

If you just want to check that an exception was thrown, and not a specific type / message, you can simply filter the html for "Exception". Remember that during production the user will see only “Oops, an error occurred”, the word “Exception” will not be present.

+1
source

Thanks to @redbirdo with his last answer, I found a solution without disrupting the controllers. I just changed a few lines in the templates.

Controllertest

 public function testUpdate() { $client = static::createClient(); $session = $client->getContainer()->get('session'); $session->set('testActionForm', 'abc'); $session->save(); // This line is important or you template won't see the variable // ... tests } 

View

 {% if app.session.has('testActionForm') %} {% set actionForm = path('parkingzone_update', { 'id': app.session.get('testActionForm') }) %} {% else %} {% set actionForm = path('parkingzone_update', { 'id': entity.id }) %} {% endif %} <form action="{{ actionForm }}" {{ form_enctype(form) }} method="POST" class="form-horizontal"> // ... rest of the form 
+1
source

All Articles