Tuesday, August 16, 2011

Selenium user interface test with PHPUnit

I used to simply use Selnium IDE, a firefox plugin, for a website's interface test. However, if we want to integrate the user interface tests into our continuous integration system, we can use Selenium RC server to do automated user interface tests in our continuous integration system. Selenium runs all the tests directly in a browser, just as a real user is browsing the website. PHPUnit provides the functions we need to talk to Selenium RC server and we can write user interface test cases just like usual unit test cases in PHPUnit.

First we must download Selenium Server from here: http://seleniumhq.org/download/, my current version is the latest 2.3.0. It is a single .jar file: selenium-server-standalone-2.3.0.jar. To start the server, we must run this command:

java -jar /path/to/selenium-server-standalone-2.3.0.jar

That is it. We have setup our testing server.

Now, write our first test case, we just want to browse http://localhost/ and check if it works! :D

TestLocalhost.php

<?php
class TestLocalhost extends PHPUnit_Extensions_SeleniumTestCase
{
  protected function setUp()
  {
    $this->setBrowser("*firefox");
    $this->setBrowserUrl("http://localhost/");
  }

  public function testLocalhost()
  {
    $this->open("/");
    $this->verifyTextPresent("It works!");
  }
}
?>

And then we can just run the test with the command:

phpunit TestLocalhost.php

Let's check through this test case.

In setUp method, we set up the browser we want to use: $this->setBrowser("*firefox"). This tells selenium to use firefox for testing. We can setup other browsers as long as they are installed on our system, some of them are:

  *firefox
  *chrome
  *iexplore
  *safari
  *opera
What if we want to run the tests on a series of browsers? We can declare a public static $browsers array in the test class:

class TestLocalhost extends PHPUnit_Extensions_SeleniumTestCase
{
    public static $browsers = array(
      array(
        'name'    => 'Firefox on Linux',
        'browser' => '*firefox',
        'host'    => 'localhost',
        'port'    => 4444,
        'timeout' => 30000,
      ),

      array(
        'name'    => 'Chrome on Linux',
        'browser' => '*chrome',
        'host'    => 'localhost',
        'port'    => 4444,
        'timeout' => 30000,
      ),
    );

  protected function setUp()
  {
    $this->setBrowserUrl("http://localhost/");
  }

  public function testLocalhost()
  {
    $this->open("/");
    $this->verifyTextPresent("It works!");
  }
}
?>

Now, Selenium will run the tests through all browsers declared in the static $browsers array.

$this->setBrowserUrl("http://localhost/"); set up the base Url of our web application.

Now we have one test case testLocalhost(). $this->open("/") tells we open the root of our web site first: http://lcoalhost/.  $this->verifyTextPresent("It works!") will verify the text 'It works' should be presented after we go to http://lcoalhost/,

Now let's look at another example:

<?php
class TestWebApp extends PHPUnit_Extensions_SeleniumTestCase
{
  protected function setUp()
  {
    $this->setBrowser("*chrome");
    $this->setBrowserUrl("http://webapp/");
  }

  public function testLogin()
  {
    $this->open("/");
    $this->type("id=txtUserId", "username");
    $this->type("id=txtPassword", "wrongpassword");
    $this->click("id=frmLoginButton");
    $this->waitForPageToLoad("30000");
    $this->verifyTextPresent("Username or Password incorrect");
    $this->type("id=txtPassword", "correctpassword");
    $this->click("id=frmLoginButton");
    $this->waitForPageToLoad("30000");
    $this->assertEquals("WebApp 3.3.0", $this->getTitle());
  }
}
?>

This simple test case test login of a web application.    $this->type("id=txtUserId", "username") will type the text 'username' into the text field with id=txtUserId, and then type the text 'wrong password' into the password text field. $this->click("id=frmLoginButton") will do a click action on the login button. $this->waitForPageToLoad("30000"), well, quite self explained. Since we enter a wrong password, we expect the text "Username or Password incorrect" is displayed on the web page, so we do $this->verifyTextPresent("Username or Password incorrect");

Writing all these test cases for a whole web site is quite time consuming. We can use Selenium IDE to record all our tests and export them in PHPUnit format, which makes our life much easier.

1 comment:

MK said...

Hi Henry,
I like the tutorial and your motto (vide God's existence). Keep it up!

Michal