Testing Symfony Forms with Complex Field Types


Introduction

Creating functional tests for controllers that have forms with complex field types can be hard to understand at first, but once you understand the basic concepts, they become easier. This article explains a simple example that I had to test with an application i developed.

Client Crawler and Redirects

For my application, I needed to click on a link to get to the authentication page, and then after authentication succeeds, that controller redirects back to the homepage (with cookies created). Since a typical test case looks for a 200 response code, when redirected, you will actually get a 302 response code instead. To workaround this in Symfony, you’ll need to follow redirects. Sample client code would look like the following:

...
public function testLogin(){
    $client = static::createClient(); // Create crawler client.
    $client->followRedirects(); // After authenticating will redirect.
    ...

So in the above code, the followRedirects() method is used when your client is expecting to be redirected on a successful response as per the documentation.

Getting Form Page and Node

After creating the client, I needed to click a link to go to the login page and then assert that it is the correct page and get a crawler node (the login button). Example code is as follows:

...
$crawler = $client->request('GET', '/'); // Get homepage.
$loginLink = $crawler->selectLink('Login link')->link();
$crawler = $client->click( $loginLink ); // Click link.

$this->assertContains(
    'Enter your details',
    $client->getResponse()->getContent()
);

$authCrawlerNode = $crawler->selectButton('Login'); // Get form button.
...

The above code gets the homepage content, looks for a link called “Login link” then clicks it; and then finally asserts the resultant page contains “Enter your details”. Then it gets the crawler node a button labelled “Login”.

The Login Form

My form is interesting in that it asks for first & last names and the person’s date of birth. See the screenshot below:

Authenticate

Notice the date of birth is a drop-down list. This is a complex type of input I was mentioning at the beginning of this article.

The Symfony testing documentation only briefly describes how to perform testing on some complex form input types, so you really have to experiment to figure things out. For example, in my case, based on the documentation I could write the following example code:

...
$form = $authCrawlerNode->form(); // Get the form.

// Fill in the Authentication form details.
$form['form[f_name]'] = 'John';
$form['form[l_name]'] = 'Doe';
$form['form[dob][day]']->select('2');
$form['form[dob][month]']->select('7');
$form['form[dob][year]']->select('1981');
...

However, that’s not the easiest way to write the code.

You may wonder how I figured out the form input names to use and the values. If you use FireFox, right-click on the day drop-down list element and select “Inspect element” and you’ll see something like the following:

dob_day_inspect

Notice the name value. You can copy and paste that into your code. The name is a composite of the form name + form field type name + list item. So in my case, my form was called “form”, and the field type name is “dob”, and the list item is the “day” field.

Easier Way to Write Code

An easier way to write the code is put the values you want to enter in the form in an array, and use the crawler node to do this like so:

...
$form = $authCrawlerNode->form(array(
      'form[f_name]' => 'John',
      'form[l_name]' => 'Doe',
      'form[dob][day]' => '2',
      'form[dob][month]' => '7',
      'form[dob][year]' => '1981'
));

$crawler = $client->submit( $form );
...

Notice you don’t call the select method on the drop-down list items, but just enter the correct values you need.

You can simplify even further by just using similar code and the client submit method like so:

...
$client->submit( $authCrawlerNode->form(array(
     'form[f_name]' => 'John',
     'form[l_name]' => 'Doe',
     'form[dob][day]' => '2',
     'form[dob][month]' => '1',
     'form[dob][year]' => '1981'
)));
...

Verifying Submission

Finally after submitting the form, you need to assert that the user was indeed authenticated. The code would look like the following:

   ...
   $client->submit( $authCrawlerNode->form(array(...
   ...

   $this->assertEquals(200, $client->getResponse()->getStatusCode());
}

Since the follow redirects has been enabled, then then expected response code of 200 should be expected. You should verify any failures of the test case. A lot of times it will be the incorrect array values specified. All the above code is from working real examples.

I hope this is a good example for someone to use.

Advertisements

About Alvin Bunk
Hi, I'm a software developer at Taft College.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: