Selenium and symfony Integeration – the Non-plugin Approach (Tutorial)

I have been writing a lot test cases for a large web project built by symfony. From time to time I found the build-in testers are not sufficient to test my application thorough enough. Often times it is not possible to simulate the way a user would interact with the application. For this reason, I have turned to Selenium.

Though it takes some extra work to get it to work with symfony's Lime test framework, the result turns out to be very satisfactory. The following is for those who are interested at the integration process. All code examples shown here are written using a Linux box, but they should also work under Window environment.

Running the Server - Selenium 2 RC

The tool we need from SeleniumHQ is "Selenium 2". Even though it is says "2.0 alpha 5" on their download page as time of writing, it is actually stable enough for our purpose. You may install "Selenium IDE" to your Firefox, but I don't find it is very useful. I only used it once a while. All downloads can be found on Selenium's site.

After you have downloaded the server package, unzip it and start the server by running:

You should see some output from your command prompt similar below:

Getting Client Library for PHP

Go to the download site again, download Selenium RC (selenium-remote-control-1.x.x.zip). Note that we only need the client library, not the server package since we are using Selenium 2 server.

Unzip the file and navigate to selenium-php-client-driver-1.x.x/PEAR/Testing/, copy the all files under it to SF_ROOT_DIR/lib/test/selenium/. For my personal preference and to comply with symfony's convention, I have renamed the class files:

Selenium.php => Testing_Selenium.class.php
Exception.php => Testing_Selenium_Exception.class.php

I have placed two file to the same directory level:
SF_ROOT_DIR/lib/test/selenium/Testing_Selenium.class.php
SF_ROOT_DIR/lib/test/selenium/Testing_Selenium_Exception.class.php

Open SF_ROOT_DIR/lib/test/selenium/Testing_Selenium.class.php, and remove the line require_once 'Testing/Selenium/Exception.php';, since all class files will be auto loaded by symfony.

There's no whatsoever reason to use PEAR_Exception, so I have changed the content of Testing_Selenium.class.php to as the following:

Extends Client Library

You may use the client library you just downloaded from SelemniuHQ, but I would like to separate my own code from the library code, so choose to extend the client library. I overwrote the doCommand method, to capture the output returned by Selenium server, also to call the callback function which outputs some useful message via symfony's Lime output.

Additional Selenium related methods should be placed into this class (or Testing_Selenium if you don't do the inheritance way). For example, I have added clickAndWait method, which will be used extensively in our testing code.

Extends sfBrowser

We extends sfBrowser to handle initialization and destruction of the Selenium test session. Here's the example code how to do it:

Extends sfTestFunctional

I extended sfTestFuncional to make a proxy method which returns mySelenium instance, and to support better code auto-completion.

Create a Selenium Tester

I have created a tester which provides a number of methods that perform the testing job and output meaningful feedback information when the test is run.

Note that I overwrote the __call method, so when you call a method which does not exist in the tester class but in the mySelenium class, the method in mySelenium class will get invoked.

Read more about writing a new symfony tester

Create a Factory Class to construct Functional Test Class Instance

You can skip this step, but you are going to use this code in almost all of your test cases. So I would suggest you use the following code as I did:

Almost there, please bear with me.

Create a Test Case

Create a new file under SF_ROOT_DIR/test/functional/[application_name]/homeTest.php, replace the "[application_name]" with the symfony application you wan to test. Create a test case a the following:

To run the test case, execute the following (assume that application name is "frontend"):

You should see Firefox browser showing up and responding to your test commands.

Read more about Selenium commands

Selenium Test Case Writing Consideration

The key to writing Selenium test cases in symfony is not to pay too much attention to the underlaying implementation of the web application. Let your test cases test the web application as if a human user would. By doing so will ease your test creation because you will not have follow tightly what you read on the official documentation of symfony. I use almost only the $b->with('selenium')->begin() tester from now on.

When you are writing a test case, open a page you want to test, and imagine what the user would do, and then translate all the user actions to Selenium commands. For example,

User Selenium Test Code in symfony
Open a URL ->open()
Click a link ->clickAndWait()
Type some thing into text filed/area ->type() / ->typeMany()
Check a checkbox / radio button ->check()
Upload a file ->attachFile()

And so on...

You can use isElementPresent() and isTextPresent() to verify the resulting page.

Functional Bootstrap

The bootstrap file should be generated by symfony at SF_ROOT_DIR/test/bootstrap/functional.php by default. I post it here in case you don't have this file for some reason.

Source Download

To be added.

12 Comments Selenium and symfony Integeration – the Non-plugin Approach (Tutorial)

  1. YoMan

    You are so smart ;-)
    Could you also post the code for the myBrowser class.
    I guess you extend sfBrowser in a certain way.
    Tanks man!

  2. Cuong

    I thought I added something to the class too, but it turns out to be an empty class :) See Extends sfBrowser section for the class code.

  3. ed0t

    Excellent post!
    But i am getting an error.
    When i try to run my test it says that the startWithOptions method in myBrowserSelenium class does not exists.

    After commenting those three lines i have another error:
    it says that myBrowserSelenium class does not have a getBrowser method which is expected by sfTestFunctionalBase.class.php.

    Any clue?

    Thanks

  4. Guto

    Hi!
    I followed the tutorial...
    when i get to the "test:functional frontend home" part my output result is:

    Fatal error: Class 'myBrowserSelenium' not found in SF_ROOT_DIR\lib\test\myBrowserFactory.class.php on line 11

    What am i doing wrong?
    Thanks.

  5. Guto

    Hi Cuong.
    i did as you told and everything seens to be like you describe..
    i think that symfony is not including the classes in the 'SF_ROOT_DIR/lib/test'
    I ran other tests with my lib/model classes, the autoload is fine and include all my lib/model classes, but it doesn't found the myBrowserFactory.
    any ideas?

  6. Guto

    $b = new myBrowserSelenium(
    sfConfig::get('app_domain'),
    null,
    array(),
    array('browserType' => '*firefox')
    );

    the functional test crashs in the factory.

  7. Cuong

    Your previous comment mentioned

    Fatal error: Class 'myBrowserSelenium' not found in SF_ROOT_DIR\lib\test\myBrowserFactory.class.php on line 11

    which indicates myBrowserSelenium not found, but myBrowserFactory is indeed loaded.

    Please verify again which class is not loaded. From the error message you got, is it 'myBrowserSelenium not found' or 'myBrowserFactory not found'?

  8. syed

    I'm getting this error

    fopen(http://:/selenium-server/driver/?cmd=getNewBrowserSession&1=%2Achrome&2=http%3A%2F%2F): failed to open stream: Success in /opt/lampp/htdocs/project/trunk/lib/test/selenium/mySelenium.class.php on line 25

    cannot connected to selenium server

    Any thoughts how to resolve this. I removed the added (:/) in fopen that you can notice above... but then I started getting the following errors

    PHP Warning: fopen(): php_network_getaddresses: getaddrinfo failed: Name or service not known in /opt/lampp/htdocs/project/trunk/lib/test/selenium/mySelenium.class.php on line 25

    Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *