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.
Found a typo or you think this page needs improvement?
Edit it on github !
PHPUnit documentation on Selenium
The direct link for PHPUnit and Selenium is : http://www.phpunit.de/manual/current/en/selenium.html
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)));
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
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); }
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.
Signup or Login in order to comment.