Functional testing with PhantomJS, Selenium and PHPUnit

TL;DR

How to setup Selenium & PhantomJS

For my test purposes I use Selenium Standalone Server; this is a Java application so requires: apt-get install openjdk-7-jre-headless

PhantomJS is a single binary with no requirements (pretty awesome), which can be ran as a headless browser or with WebDriver.

During my setup I had numerous issues/bugs using PhantomJS WebDriver (it uses GhostDriver) with Selenium, and don't currently recommend you try it this way.

As we'll be using PhantomJS without WebDriver we only need one command to get Selenium up and running: java -jar /usr/local/bin/selenium-server-standalone-2.53.1.jar -log /tmp/selenium.log -Dphantomjs.binary.path=/usr/local/bin/phantomjs

Each of our tests will connect to Selenium and post messages on how to handle the configured browser (phantomjs in our case, other brands are available).

How to setup PHPUnit

Your composer.json will require phpunit/phpunit and phpunit/phpunit-selenium to run tests with Selenium.

All unit tests will need to extend PHPUnit_Extensions_Selenium2TestCase and requires a setUp method.

Here is the setUp method I use:

public function setUp() {
$this->setHost('192.168.50.50'); // Vagrant private IP, so we can run the tests from our local machine
$this->setPort(4444); // Port selenium listens on
$this->setBrowser('phantomjs'); // Browser to use (you can use Chrome/Firefox if you set those drivers up separately)
$this->setBrowserUrl('https://twitter.com'); // Base URL I want to test. This would be your project's dev URL
$this->prepareSession()->currentWindow()->size(array(
'width' => 1024,
'height' => 768,
));
}

As most sites use responsive CSS I explicitly set the width and height of my window, to ensure that it doesn't hide search boxes and the like behind the hamburger menu.

Hidden elements are not accessible from Selenium.

How to write tests

All tests should extend PHPUnit_Extensions_Selenium2TestCase, and have a setUp method. Currently I also always use tearDown method to ensure resources are cleaned up and we're good to go:

public function tearDown() {
$this->closeWindow();
}

Most time writing these tests is finding the methods you need to get what you want, so here's a cheatsheet: