This is the supporting material for my session at True North PHP. It was certainly not a tutorial, but instead was a quick introduction to things, and plant some of my favourite rants into people's (unsuspecting) heads.
- WebDriver is just a library. It is not a framework
- It is also a [draft] W3C standard
- It will not test your application, it will drive your browser
- Geeks are bad at naming. Names Matter somewhat explains the various moving parts -- and how to refer to things.
- All (current) PHP bindings use Remote WebDriver
- WebDriver for PHP is a very splinter-y place; I use my fork of Facebook's implementation -- but spend the time to experiment with them all and see which one aligns with your own set of opinions. There is lots of howto-ish things in that repo...
- There are two halves, well, its more like 20/80 parts; the 'session' and 'elements'
All the moving parts for PHPWebDriver and this project uses are installable through PEAR.
$ sudo pear channel-discover pear.phpunit.de
$ sudo pear install phpunit/PHPUnit
$ sudo pear channel-discover element-34.github.com/pear
$ sudo pear install -f element-34/SaunterPHP
$ sudo pear install -f element-34/PHPBrowserMobProxy
When you are writing scripts you will end up with a script and one or more page object.
Scripts...
- are collected by the runner
- executes methods (actions) on page objects
- contains asserts
- are pretty un-interesting
Page Objects...
- represent either a full page or part of a page
- find and interact with elements
- does not contain elements that are in other elements
- synchronize
Synchronization is hard (let's go shopping!). The important thing to remember is Explicit is better than Implicit.
$w = new \PHPWebDriver_WebDriverWait($this->session, 30, 0.5, array("locator" => $this->locators['fancy iframe']));
$iframe = $w->until(
function($session, $extra_arguments) {
list($type, $string) = $extra_arguments['locator'];
return $session->element($type, $string);
}
);
If you are not responsible for developing something on your application, it shouldn't be there in your automation environment. Use feature switches, but if you don't have that, blacklist through a proxy.
$driver = new \PHPWebDriver_WebDriver();
self::$client = new \PHPBrowserMobProxy_Client("localhost:8080");
$additional_capabilities = array();
$proxy = new \PHPWebDriver_WebDriverProxy();
$proxy->httpProxy = self::$client->url;
$proxy->add_to_capabilities($additional_capabilities);
self::$session = $driver->session('firefox', $additional_capabilities);
self::$client->blacklist('.*\/favicon\.ico/.*', 306);
self::$client->blacklist('.*\.facebook\.net/.*', 306);
self::$client->blacklist('.*\.twitter\.com/.*', 306);
self::$client->blacklist('.*\.github\.com/.*', 306);
Welcome to the future... where Canvas and ^$(*@)-ing JS widgets rule the world. These are black boxes [of pain].
$this->session->execute(array(
"script" => 'return document.title;',
"args" => array()
)
);
The args key can also take a reference to an Element that can be used in the script itself. Ooooo. Meta.
$options = array("chain" => $chain, "attrName" => $property);
$this->session->execute(array(
"script" => 'return arguments[0].fp_getPropertyValue(arguments[1]);',
"args" => array(array("ELEMENT" => $this->movie->getID()),
$options)
));
Just remember to return a value if you are not just poking things.
WebDriver has desired capabilities and required capabilities. (Or will have required capabilities soon-ish.)
$this->session = self::$driver->session("android");
$this->session = self::$driver->session("iphone");
$this->session = self::$driver->session("ipad");
You do not want to deal with the browser permutation madness... outsource it! To something like Sauce Labs. Since WebDriver [on PHP] is Remote WebDriver, just point it at their server.
$username = "yourusername";
$key = "your key";
$command_executor = "http://" . $username . ":" . $key . "@ondemand.saucelabs.com:80/wd/hub";
self::$driver = new PHPWebDriver_WebDriver($command_executor);
disclaimer: I'm a Sauce Labs partner, ask me for my partner discount code
WebDriver is actually really low level and you will want to write some helper-ish things around it. SaunterPHP is one such framework based on the last decade of me doing this sort of thing. (And Open Source. Of course!)