Actions

Difference between revisions of "Writing System Tests for Joomla! - Part 1"

From Joomla! Documentation

m
Line 9: Line 9:
 
See [[System Testing]] for a good primer on system tests.
 
See [[System Testing]] for a good primer on system tests.
  
 
+
== Tips & Tricks ==
== Items to Note ==
+
* It is recommended to update your local copy of Joomla! to the latest build before writing or using any tests.
* It's recommended to update your local copy of 1.6 to the latest build before writing any tests.
+
* Some of the tests written for Joomla! version 1.6 use the included sample data. You need to install the sample data to run these tests.
 +
* System tests are loosely dependent on the default templates. The systems tests written for version 1.6 use the rhuk_milkyway (front end) and bluestork (back end) templates.
 +
* It is recommended that tests "undo" any changes made to the site. This can be done as part of the test. Other tests may encounter issues if artifacts of other tests are present.
 
* The test are written in PHP and we are using Selenium RC to execute them later. You will need to select the PHP Selenium RC Format in Selenium IDE. Go to Options -> Format -> PHP Selenium RC
 
* The test are written in PHP and we are using Selenium RC to execute them later. You will need to select the PHP Selenium RC Format in Selenium IDE. Go to Options -> Format -> PHP Selenium RC
 
* More information about writing test can be found at [http://www.phpunit.de/manual/current/en/writing-tests-for-phpunit.html http://www.phpunit.de/manual/current/en/writing-tests-for-phpunit.html]
 
* More information about writing test can be found at [http://www.phpunit.de/manual/current/en/writing-tests-for-phpunit.html http://www.phpunit.de/manual/current/en/writing-tests-for-phpunit.html]
 
  
 
== Creating a Basic System Test ==
 
== Creating a Basic System Test ==

Revision as of 09:03, 12 February 2010

Contents

Introduction

As documented at Running_Automated_Tests_for_Version_1.6, Joomla! 1.6 now includes a library of system tests. This document details the steps necessary to create system tests using Selenium IDE. You will also need to run Firefox and install the Selenium IDE add-on to Firefox. Instructions for this can be found in the System Testing article.

NOTE: This article assumes that you have read the aforementioned articles and have PHPUnit and Selenium set up and working properly.

Overview

Writing a system test essentially entails converting a Selenium IDE test to a format that we can easily integrate into the Joomla! test suite. See System Testing for a good primer on system tests.

Tips & Tricks

  • It is recommended to update your local copy of Joomla! to the latest build before writing or using any tests.
  • Some of the tests written for Joomla! version 1.6 use the included sample data. You need to install the sample data to run these tests.
  • System tests are loosely dependent on the default templates. The systems tests written for version 1.6 use the rhuk_milkyway (front end) and bluestork (back end) templates.
  • It is recommended that tests "undo" any changes made to the site. This can be done as part of the test. Other tests may encounter issues if artifacts of other tests are present.
  • The test are written in PHP and we are using Selenium RC to execute them later. You will need to select the PHP Selenium RC Format in Selenium IDE. Go to Options -> Format -> PHP Selenium RC
  • More information about writing test can be found at http://www.phpunit.de/manual/current/en/writing-tests-for-phpunit.html

Creating a Basic System Test

By leveraging the Selenium_Test_Case_Methods present in Joomla! 1.6, we can create a simple automated system test with just a few lines of code.

For example, to test logging into and then out of the back end, we could use the following code:

<?php
require_once 'SeleniumJoomlaTestCase.php';

class ControlPanelExample extends SeleniumJoomlaTestCase
{
	function testExample()
	{
		$this->setUp();
		$this->doAdminLogin();
		$this->doAdminLogout();
	}
}
?>
  • require_once 'SeleniumJoomlaTestCase.php';? allows use to utilize the methods that belong to the Selenium Joomla Test Case class
  • class ControlPanelExample extends SeleniumJoomlaTestCase extends a class into our SeleniumJoomlaTestCase to implement the client/server protocol to talk to Selenium RC as well as specialized assertion methods for web testing.
  • Each test is written as a function.
  • $this->setUp(); envokes our configuration from tests/system/servers/configdef.php see Running_Automated_Tests_for_Version_1.6#Create_a_Selenium_Configuration_File
  • $this->doAdminLogin(); is a Selenium Joomla Test Case method that logs into the back end.
  • $this->doAdminLogoutn(); is a Selenium Joomla Test Case method that logs out of the back end.


Creating an Intermediate System Test

In this test, we are going to execute the test described at Intermediate_Selenium_Example and integrate it into the system tests suite in 1.6.

1. Open Joomla! 1.6 in FireFox and navigate to the home page.

2. Open Selenium IDE by going to the Tools menu and select Selenium IDE. Selenium will start recording automatically and the red record button will appear highlighted (see image below).

3. Verify that the Base URL displayed in Selenium, next to the record button, is that of your test site.

4. Perform the test by following the instructions described above (Note: there are some slight variations between the test described above and the one we performed. This is due to variation of 1.6 during the development process).

5. When you have completed your test, click the the red record button (see image below) to stop recording.


Selenium-screen.png


6. Once recording has stopped, create a new PHP file in Eclipse (or your favorite code editor) and copy / paste the code from Selenium IDE to there. The contents of the example test is:

<?php

require_once 'PHPUnit/Extensions/SeleniumTestCase.php';

class Example extends PHPUnit_Extensions_SeleniumTestCase
{
  function setUp()
  {
    $this->setBrowser("*chrome");
    $this->setBrowserUrl("http://change-this-to-the-site-you-are-testing/");
  }

  function testMyTestCase()
  {
    $this->open("/workspace/joomla-1-6-source/administrator/index.php?option=com_login");
    $this->type("mod-login-username", "admin");
    $this->click("link=Log in");
    $this->waitForPageToLoad("30000");
    $this->click("link=User Manager");
    $this->waitForPageToLoad("30000");
    $this->click("//li[@id='toolbar-new']/a/span");
    $this->waitForPageToLoad("30000");
    $this->type("jform_name", "My Test User");
    $this->type("jform_username", "TestUser");
    $this->type("jform_password", "password");
    $this->type("jform_password2", "password");
    $this->type("jform_email", "test@example.com");
    $this->click("1group_2");
    $this->click("link=Save & Close");
    $this->waitForPageToLoad("30000");
    try {
        $this->assertTrue($this->isTextPresent("Item successfully saved."));
    } catch (PHPUnit_Framework_AssertionFailedError $e) {
        array_push($this->verificationErrors, $e->toString());
    }
    $this->type("search", "TestUser");
    $this->click("//button[@type='submit']");
    $this->waitForPageToLoad("30000");
    try {
        $this->assertTrue($this->isTextPresent("TestUser"));
    } catch (PHPUnit_Framework_AssertionFailedError $e) {
        array_push($this->verificationErrors, $e->toString());
    }
    $this->click("link=Log out");
    $this->waitForPageToLoad("30000");
    $this->click("link=Go to site home page.");
    $this->waitForPageToLoad("30000");
    $this->type("modlgn_username", "TestUser");
    $this->type("modlgn_passwd", "password");
    $this->click("Submit");
    $this->waitForPageToLoad("30000");
    try {
        $this->assertTrue($this->isTextPresent("My Test User"));
    } catch (PHPUnit_Framework_AssertionFailedError $e) {
        array_push($this->verificationErrors, $e->toString());
    }
    $this->click("link=Logout");
    $this->waitForPageToLoad("30000");
    $this->click("//button[@type='submit']");
    $this->waitForPageToLoad("30000");
    $this->click("link=Site Administrator");
    $this->waitForPageToLoad("30000");
    $this->type("mod-login-username", "admin");
    $this->click("link=Log in");
    $this->waitForPageToLoad("30000");
    $this->click("//img[@alt='User Manager']");
    $this->waitForPageToLoad("30000");
    $this->click("cb0");
    $this->click("link=Delete");
    $this->waitForPageToLoad("30000");
    try {
        $this->assertTrue($this->isTextPresent("1 item(s) successfully deleted."));
    } catch (PHPUnit_Framework_AssertionFailedError $e) {
        array_push($this->verificationErrors, $e->toString());
    }
    $this->click("link=Log out");
    $this->waitForPageToLoad("30000");
  }
}
?>

As noted in Intermediate_Selenium_Example Selenium does not record passwords that are entered. Additionally, we want to utilize Selenium_Test_Case_Methods to prevent coding path or login information in the test.


1. The first step in integrating this test into tests/suite is to call SeleniumJoomlaTestCase.php so that we can utilize the Selenium Test Case Methods. To do this, we will replace the first portion of our code:

<?php
require_once 'PHPUnit/Extensions/SeleniumTestCase.php';

with the following code

<?php
require_once 'SeleniumJoomlaTestCase.php';
  • We can now start utilizing the Selenium Joomla Test Case Methods.


2. We will now extend a class into our SeleniumJoomlaTestCase to implement the client/server protocol to talk to Selenium RC as well as specialized assertion methods for web testing.

This is done by replacing:

class Example extends PHPUnit_Extensions_SeleniumTestCase
{

with

class User0002Test extends SeleniumJoomlaTestCase
{
  • The top portion of our test now looks like:
<?php
require_once 'SeleniumJoomlaTestCase.php';

class User0002Test extends SeleniumJoomlaTestCase
{

  function testMyTestCase()
  {
  • At this point, running the test would successfully launch a two browsers instances, one for Selenium Remote Control and one for test itself, but it would fail due to the lack of the password being recorded. Additionally, we want to be sure that none of the path information is hard coded in the test.

3. To improve our code, and achieve what we noted above, we would delete the function below:

function setUp()
  {
    $this->setBrowser("*chrome");
    $this->setBrowserUrl("http://change-this-to-the-site-you-are-testing/");
  }

and add the following to our testMyTestCase function

	$this->setUp();

Additionaly, we can replace the following line of code:

$this->open("/workspace/joomla-1-6-source/administrator/index.php?option=com_login");

with

$this->gotoAdmin();

As you can see, this will prevent us from hard coding the path into the test.

We can now replace

$this->type("mod-login-username", "admin");
    $this->click("link=Log in");
    $this->waitForPageToLoad("30000");

with

$this->doAdminLogin();

At this point, running our test would succeed up until the point where we need to log into the administrative interface the second time. We'll fix that by replacing:

    $this->type("mod-login-username", "admin");
    $this->click("link=Log in");
    $this->waitForPageToLoad("30000");

with

$this->doAdminLogin();

Running our test should now successfully create, verify the creation of, and delete our new test user.

  • To rely to the user what is happening, we want to add echo statements to our code.

Our final test looks like:

<?php

require_once 'SeleniumJoomlaTestCase.php';

class User0002Test extends SeleniumJoomlaTestCase
{
  function testMyTestCase()
  {
    echo("Starting testMyTestCase\n");
    $this->setUp();	
    $this->gotoAdmin();
    $this->doAdminLogin();
    $this->click("link=User Manager");
    $this->waitForPageToLoad("30000");
    echo("Add new user.\n");
    $this->click("//li[@id='toolbar-new']/a/span");
    $this->waitForPageToLoad("30000");
    $this->type("jform_name", "My Test User");
    $this->type("jform_username", "TestUser");
    $this->type("jform_password", "password");
    $this->type("jform_password2", "password");
    $this->type("jform_email", "test@example.com");
    $this->click("1group_2");
    $this->click("link=Save & Close");
    $this->waitForPageToLoad("30000");
    echo("Verify existence of new user.\n");
    try {
        $this->assertTrue($this->isTextPresent("Item successfully saved."));
    } catch (PHPUnit_Framework_AssertionFailedError $e) {
        array_push($this->verificationErrors, $e->toString());
    }
    $this->type("search", "TestUser");
    $this->click("//button[@type='submit']");
    $this->waitForPageToLoad("30000");
    try {
        $this->assertTrue($this->isTextPresent("TestUser"));
    } catch (PHPUnit_Framework_AssertionFailedError $e) {
        array_push($this->verificationErrors, $e->toString());
    }
    $this->click("link=Log out");
    $this->waitForPageToLoad("30000");
    echo("Go to home page.\n");    
    $this->click("link=Go to site home page.");
    $this->waitForPageToLoad("30000");
    echo("Log in as TestUser.\n");    
    $this->type("modlgn_username", "TestUser");
    $this->type("modlgn_passwd", "password");
    $this->click("Submit");
    $this->waitForPageToLoad("30000");
    echo("Verify existence of new user.\n");    
    try {
        $this->assertTrue($this->isTextPresent("My Test User"));
    } catch (PHPUnit_Framework_AssertionFailedError $e) {
        array_push($this->verificationErrors, $e->toString());
    }
    $this->click("link=Logout");
    $this->waitForPageToLoad("30000");
    $this->click("//button[@type='submit']");
    $this->waitForPageToLoad("30000");    
    $this->click("link=Site Administrator");
    $this->waitForPageToLoad("30000");
	$this->doAdminLogin();
    $this->click("//img[@alt='User Manager']");
    $this->waitForPageToLoad("30000");
    $this->click("cb0");
    echo("Delete new user.\n");    
    $this->click("link=Delete");
    $this->waitForPageToLoad("30000");
    try {
        $this->assertTrue($this->isTextPresent("1 item(s) successfully deleted."));
    } catch (PHPUnit_Framework_AssertionFailedError $e) {
        array_push($this->verificationErrors, $e->toString());
    }
    $this->click("link=Log out");
    $this->waitForPageToLoad("30000");
  }
}
?>