Functional Testing

Before reading this section, it is recommended that you read the Selenium documentation and the PHPUnit documentation first. We summarize in the following the basic principles of writing a functional test in Yii:

  • Like unit test, a functional test is written in terms of a class XyzTest which extends from CWebTestCase, where Xyz stands for the class being tested. Because PHPUnit_Extensions_SeleniumTestCase is the ancestor class for CWebTestCase, we can use all methods inherited from this class.

  • The functional test class is saved in a PHP file named as XyzTest.php. By convention, the functional test file may be stored under the directory protected/tests/functional.

  • The test class mainly contains a set of test methods named as testAbc, where Abc is often the name of a feature to be tested. For example, to test the user login feature, we can have a test method named as testLogin.

  • A test method usually contains a sequence of statements that would issue commands to Selenium RC to interact with the Web application being tested. It also contains assertion statements to verify that the Web application responds as expected.

Before we describe how to write a functional test, let's take a look at the WebTestCase.php file generated by the yiic webapp command. This file defines WebTestCase that may serve as the base class for all functional test classes.

define('TEST_BASE_URL','http://localhost/yii/demos/blog/index-test.php/');
 
class WebTestCase extends CWebTestCase
{
    /**
     * Sets up before each test method runs.
     * This mainly sets the base URL for the test application.
     */
    protected function setUp()
    {
        parent::setUp();
        $this->setBrowserUrl(TEST_BASE_URL);
    }
 
    ......
}

The class WebTestCase mainly sets the base URL of the pages to be tested. Later in test methods, we can use relative URLs to specify which pages to be tested.

We should also pay attention that in the base test URL, we use index-test.php as the entry script instead of index.php. The only difference between index-test.php and index.php is that the former uses test.php as the application configuration file while the latter main.php.

We now describe how to test the feature about showing a post in the blog demo. We first write the test class as follows, noting that the test class extends from the base class we just described:

class PostTest extends WebTestCase
{
    public $fixtures=array(
        'posts'=>'Post',
    );
 
    public function testShow()
    {
        $this->open('post/1');
        // verify the sample post title exists
        $this->assertTextPresent($this->posts['sample1']['title']);
        // verify comment form exists
        $this->assertTextPresent('Leave a Comment');
    }
 
    ......
}

Like writing a unit test class, we declare the fixtures to be used by this test. Here we indicate that the Post fixture should be used. In the testShow test method, we first instruct Selenium RC to open the URL post/1. Note that this is a relative URL, and the complete URL is formed by appending it to the base URL we set in the base class (i.e. http://localhost/yii/demos/blog/index-test.php/post/1). We then verify that we can find the title of the sample1 post can be found in the current Web page. And we also verify that the page contains the text Leave a Comment.

Tip: Before running functional tests, the Selenium-RC server must be started. This can be done by executing the command java -jar selenium-server.jar under your Selenium server installation directory.

$Id$

Total 5 comments

#11367 report it
Demitri at 2013/01/09 06:20pm
If you get Selenium RC null session errors

I, like many (I suspect), followed this guide to the letter and got the following error from the Selenium server:

Java.lang.nullpointerexception: sessionId should not be null; has this session been started yet?

Lots and lots of trying and reading later, I found to my chagrin that nowhere in the CWebTestCase is a browser session actually initialized. You need to create one somewhere in your base class or a child class by using a call to prepareTestSession(). Once this is done, you can then call open() and begin testing. For example, you can simply do it right in setUp():

protected function setUp() {
        parent::setUp();
        $this->setBrowserUrl(TEST_BASE_URL);
        $this->prepareTestSession();
    }

If you need to re-use a browser session (to avoid the time-consuming process of completely restarting a web browser) you should start the server with the command line option "-reuseBrowserSession", and that way the web browser won't be shut down after every single test method runs.

#10015 report it
emix at 2012/09/29 05:28pm
my note

Remember that each test case is an independent call, so if testing password protected part of the site you must log in to the site each time. I am currently creating functional tests for my back-end module so I added this handy method to my WebTestCase:

public function openLogged($url)
{
    // login first
    $this->open('site/login');
    $this->type('name=LoginForm[username]', 'my@email.com');
    $this->type('name=LoginForm[password]', 'my_password');
    $this->clickAndWait("//input[@value='Login']");
 
    // finally open url
    $this->open($url);
}
#7119 report it
marcanuy at 2012/02/24 02:12pm
screenshots

A great feature is to capture a screenshot when a Selenium test fails. You just have to set up the following variables according to your server configuration

class WebTestCase extends CWebTestCase
{
    protected $captureScreenshotOnFailure = TRUE;
    protected $screenshotPath = '/var/www/screenshots';
    protected $screenshotUrl = 'http://localhost/screenshots';
    ...

Then when an error occurs it notifies it like:

There was 1 failure:
 
 1) WebTest::testTitle
 Current URL: http://www.yiiframework.com/blog/example/
 Screenshot: http://localhost/screenshots/33jc530f23y3mn891568ee1c582jrUc9.png
#2865 report it
RusAlex at 2011/02/18 06:05am
my note

If you are testing your app and use urlManager in your config, sometimes you will find a problem: you want to test your site independing from urlManager settings. in that way you need to extend your WebTestCase class by this method:

public function createUrl($route,$params=array()) {
 
$url = explode('phpunit',Yii::app()->createUrl($route,$params));
return $url[1];
}

and in your tests you can use next 'open' constructions:

$this->open($this->createUrl('user/view',array('id'=>$user->id)));
#2504 report it
Eliovir at 2011/01/11 06:34am
PHPUnit documentation on Selenium

The direct link for PHPUnit and Selenium is : http://www.phpunit.de/manual/current/en/selenium.html

Leave a comment

Please to leave your comment.