Anyone who has done any serious web development knows that integration testing is an important but often neglected aspect of development, especially in projects that don’t have a dedicated team for QA. Since I already use PHPUnit I thought I would try the Selenium integration it provides. Selenium provides a plaform to automate browser interactions, an add-on to PHPUnit is availble so you can create tests in PHP that will test cross browser functionality. Unfortunately the information is a bit scarce and hard to find so I’ve decided to document the process of automating browser testing using PHPUnit and Selenium.
How It All Works
Basically you have 3 components interacting with each other:
- A browser driver which allows different browsers to recieve instrcutions and exchange data;
- Language bindings which allows scripts to sends commands to interact with or extract info from the browser;
- The Selenium server which coordinates the communications between the browser driver and the language binding.
Flow Of Information For A Single Test
- PHPUnit is launched and initializes a test class that extends PHPUnit_Extensions_Selenium2TestCase which implements methods to interact with the Selenium server;
- The class sets up some parameters that instuct which browser are going to be tested and information on how to connect to the different Selenium servers to test on;
- PHPUnit initializes a test method
- The test sends instructions to load a URL through the webdriver to the Selenium server;
- The Selenium servers instruct the browser driver to loads a new browser instance and to load the specified page;
- The browser loads the page;
- Selenium server sends feedback about the page being loaded to the script;
- The test method then sends other instructions to interact with the page, such as filling out forms and submitting them;
- The flow continues back and forth until the desired steps should produce an expected result on the page;
- The tests extract information from the resulting page and checks assertions to conclude if the test passed of failed.
Setting Up The Different Parts
You can get the sample code that we will test against at geekpad/phpunit-selenium-demo project on github. You can also view the demo app here.
Running The Selenium Server
As mentionned before the Selenium server allows interactions between your test scripts and and the browser through a webdriver. The server will run on the same machine your browsers are installed on and will expose a TCP port to communicate to. The server will need a browser specific driver or plugin in order to allow testing.
There are several drivers available but for this demo we will run tests on Chrome, Internet Explorer and Firefox.
I first I would suggest creating a folder to store all Selenium related files in there. I used d:\selenium.
First you must download the Selenium server and the Internet Explorer Server from http://docs.seleniumhq.org/download/
Then you must download the Chrome server from http://chromedriver.storage.googleapis.com/index.html
You do not need to download a Firefox driver since it’s builtin the main Selenium file.
Once all files have been downloaded you must add your selenium folder to your existing path see http://www.java.com/en/download/help/path.xml
Then you can launch the server using java. With something that looks like this
c:\Program Files\Java\jre8\bin>java.exe -jar selenium-server-standalone-2.41.0.jar
Once the server is running you might need to add rules to your firewall to allow inconming connections on the port Selenium listens on (port 4444 by default).
Preparing PHPUnit
First we need to make sure we have the PHPUnit library installed. If you are using composer to manage your project then all you need is adding two lines in your composer.json’s development dependencies, one for PHPUnit’s core (phpunit/phpunit) and one for the Selenium components (phpunit/phpunit-selenium).
Here is the sample composer.json:
{ "name": "geekpad/phpunit-selenium-demo", "require": { }, "require-dev": { "phpunit/phpunit": "4.0.*", "phpunit/phpunit-selenium": "1.3.*" } }
You can then install the new modules by executing the following command in your project root’s directory
composer update -dev
You can then test that composer installed properly by running:
$./vendor/bin/phpunit --version
For other methods of installation see PHPUnit Manual, just make sure you include the Selenium module. Note that we assume the use of composer so you may need to adjust the commands to fit other installation methods.
Writting Tests
We now proceed to define some test suites and other settings in the phpunit.xml file.
<?xml version="1.0" encoding="UTF-8"?> <phpunit bootstrap="./vendor/autoload.php"> <php> <!-- define some constants to use in our test classes --> <const name="PHPUNIT_TESTSUITE_EXTENSION_SELENIUM_HOST" value="127.0.0.1"/> <const name="PHPUNIT_TESTSUITE_EXTENSION_SELENIUM_BASEURL" value="http://geekpad.ca/blog/demos/phpunit-selenium-demo"/> </php> <testsuites> <testsuite name="Integration Tests"> <directory>tests/integration/</directory> </testsuite> </testsuites> </phpunit>
A few things to note:
- On line 6, we define a constant PHPUNIT_TESTSUITE_EXTENSION_SELENIUM_HOST which should be set to the ip of the host that runs your Selenium Server, this will prevent you from hardcoding the host of the test server in your classes.
- From line 9 to 13, we define a test suite for all integration tests.
We are now ready to write our first test, integration tests should test a single functionality at a time. For example signing up to the website, posting a comment on a page, a section should not be accessible if a user is not authenticated, etc.
Our example will test that a user is authenticated when the proper credentials are provided. We create the file LoginTest.php in our tests/integration folder
<?php /** * Integration tests for login functionality */ class LoginTest extends PHPUnit_Extensions_Selenium2TestCase { /** * Defines which browsers are going to be tested * @var array */ public static $browsers = array( array( "name" => "Chrome", "browserName" => "chrome", ), array( "name" => "Internet Exprlorer", "browserName" => "iexplore", ), array( "name" => "Firefox", "browserName" => "firefox", ), ); /** * setup will be run for all our tests */ protected function setUp() { $this->setBrowserUrl(PHPUNIT_TESTSUITE_EXTENSION_SELENIUM_BASEURL); $this->setHost(PHPUNIT_TESTSUITE_EXTENSION_SELENIUM_HOST); } // setUp() /** * Test that logins work * */ public function testLoginSuccessful() { $username = 'test'; $password = 'test'; $this->url("/login.php"); $usernameInput = $this->byName("login"); $usernameInput->clear(); $this->keys($username); $usernameInput = $this->byName("password"); $usernameInput->clear(); $this->keys($password); $form = current($this->elements($this->using('css selector')->value('form.login'))); $form->submit(); /* Check for text on index page */ $h1 = current($this->elements($this->using('css selector')->value('h1'))); $this->assertEquals('Welcome to protected area!', $h1->text()); /* Check that cookie user has been set */ $authCookie = $this->cookie()->get('user'); $this->assertEquals('loggedin', $authCookie); } // testLoginSuccessful() } //LoginTest class
Running Tests
Once the Selenium server is running you can invoke your test using the following command.
./vendor/bin/phpunit --verbose --testsuite "Integration Tests"
If everything works you will see browsers open and close on your test machine and on the backend you will get an output that looks like this:
Conclusion
So this is how you can automate cross browser testing by writting simple PHP scripts, I really hope it helps to put all things together.
There is a lot more you can do you can do I suggest checking out the Selenium2 Test Case for the available methods for your tests.
Thanks for reading, feel free to comment or question if you any.