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 }