My PHPUnit-Selenium2 Cheat Sheet
Here are a few snippets of how I’ve achieved various tasks, some tricks and patterns in phpunit/phpunit-selenium v2.0 – targeting Selenium2. I’ll try to keep this updated with more techniques over time.
Screenshots
I wrote this small hook to make screenshots automatic, like they used to be. Of course you may want to put a timestamp in the file, but I usually only want the last problem.
/**
* PhpUnitSelenium v1 used to have automatic screenshot as a feature, in v2 you have to do it "manually".
*/
public function onNotSuccessfulTest(Exception $e){
file_put_contents(__DIR__.'/../../out/screenshots/screenshot1.png', $this->currentScreenshot());
parent::onNotSuccessfulTest($e);
}
Waiting for stuff
An eternal issue in automated testing is latency and timeouts. Particularly problematic in anything other than a standard onclick-pageload cycle, such as pop-up calendars or a JS app. Again I felt the move from Selenium1 to 2 made this much more clumsy, so I wrote this simple wrapper for the common wait pattern boilerplate.
/**
* Utility method to wait for an element to appear.
*
* @param string $selector
* @param int $timeout milliseconds wait cap, after which you'll get an error
*/
protected function waitFor($selector, $timeout=self::WAIT_TIMEOUT_MS){
$this->waitUntil(function(PHPUnit_Extensions_Selenium2TestCase $testCase) use($selector){
try {
$testCase->byCssSelector($selector);
} catch (PHPUnit_Extensions_Selenium2TestCase_WebDriverException $e) {
return null;
}
return true;
}, $timeout);
}
Checking for the right page, reliably.
If something goes wrong in a complex test with lots of interactions, it’s important to fail fast – for example if the wrong page loads, nothing else will work very well. So I always check the page being tested is the right page. To do this reliably, not using content or design-specific elements, I add a <body> tag “id” attribute to every page (you could use body class if you’re already using that styling technique but I tend to separate my QA tagging from CSS dependencies). Then I added this assertion to my base test case.
/**
* We use <body id="XXX"> to identify pages reliably.
* @param $id
*/
protected function assertBodyIDEquals($id){
$this->assertEquals($id, $this->byCssSelector('body')->attribute('id'));
}
Getting Value
The ->value() method was removed in Selenium v2.42.0. The replacement method is to use $element->attribute(‘value’) [source]
// old way
//$sCurrentStimulus = $this->byName('word_index')->value();
// new way
$sCurrentStimulus = $this->byName('word_index')->attribute('value');
// I actually use this now:
$sCurrentStimulus = $this->byCssSelector('input[name=word_index]')->attribute('value');
However ->value() was also a mutator (setter), which ->attribute() is not. So if you want to update a value, people say you have to resort to injecting JavaScript into the page, which I found somewhat distasteful. However luckily this is not the case for the “value” attribute specifically, according to the source code, it’s only the GET which was removed from ->value().
JSON Wire Protocol only supports POST to /value now. To get the value of an element GET /attribute/:naem should be used
So I can carry on doing this, presumably until the next update breaks everything.
$this->byName('u_first_name')->value(GeneralFixtures::VAlID_SUBJECT_USERNAME);
General Page Tests
I have one test suite that just whips through a list of all known pages on a site and scans them for errors, a visual regression smoke test for really stupid errors. It’s also easy to drop a call to this method in at the beginning of any test. When I spot other visual errors occurring, I can add them to the list.
/**
* Looks for in-page errors.
*/
protected function checkErrors() {
$txt = $this->byTag('body')->text();
$src = $this->source();
// Removed: This false-positives on the news page.
//$this->assertNotContains('error', $this->byTag('body')->text());
// Standard CI errors
$this->assertNotContains('A Database Error Occurred', $txt);
$this->assertNotContains('404 Page Not Found', $txt);
$this->assertNotContains('An Error Was Encountered', $txt);
// PHP errors
$this->assertNotContains('Fatal error: :', $txt);
$this->assertNotContains('Parse error:', $txt);
// the source might have hidden errors, but then it also might contain the word error? false positive?
// This false positives in the user form (must have validation error text!
//$this->assertNotContains('error', $this->source());
$this->assertNotContains('xdebug-error', $src); // XDebug wrapper class
}