SauceLabs provide a great hosted Selenium service for cross-browser testing in the cloud, which allows you to run a Selenium test suite against multiple browsers using the SauceLabs API and SauceConnect.

They also provide integration with PHPUnit, so that you can plug your SauceLabs Selenium tests directly into your PHPUnit test suite.

Sometimes, though, you want to run the same Selenium test suite against a single browser on your local machine, before you run the full thing against all the browsers on SauceLabs.

However, the SauceLabs PHPUnit integration requires you to extend their special base class, which they've added to their custom PHPUnit library (which you need to have installed instead of the standard PHPUnit). That base class assumes that the test suite is always going to run against the remote SauceLabs API.

To run a Selenium test suite locally, you need to be extending the standard PHPUnit Selenium base class instead of the custom SauceLabs one - but you don't want to duplicate all your tests in two class hierarchies.

How can you have a class where the parent class can be one of two different things, depending on some config setting?

The answer is by using  PHPs "class_alias" and a custom autoloader (note, "class_alias" is only available in PHP from 5.3.0 onwards). It works something like this..

Instead of extending either the special SauceLabs base class or the PHPUnit selenium base class directly, you define two different intermediate base classes - called RemoteSeleniumTestCase and LocalSeleniumTestCase - which each extend one of the base classes:

// RemoteSeleniumTestCase.php
class RemoteSeleniumTestCase extends PHPUnit_Extensions_SeleniumTestCase_SauceOnDemandTestCase 
{
    function setUp() {..}    // special setup for SauceLabs 
}
// LocalSeleniumTestCase.php
class LocalSeleniumTestCase extends PHPUnit_Extensions_SeleniumTestCase
{
    function setUp() {..}    // standard setup for local Selenium
}

The your actual test suite extends a (virtual) class - called DynamicSeleniumTestCase:

// your selenium test cases
class HomeTest extends DynamicSeleniumTestCase
{
    function testHomePage()
    {
        // check home page is there
        $this->open('/');
        $this->assertTextPresent('Welcome to BigCo');
   } 
}

So, where does DynamicSeleniumTestCase come from?

That's where class_alias and the autoloader come in - you define a custom autoloader, which will alias DynamicSeleniumTestCase to one or other of your intermediate base classes, based on the value of an environment variable (I've used RUN_SELENIUM_REMOTE):

spl_autoload_register(function($class) {
    if (strcasecmp($class, 'DynamicSeleniumTestCase') === 0) {
        $remote = getenv('RUN_SELENIUM_REMOTE');
        if ($remote) {
            require_once 'RemoteSeleniumTestCase.php';
            class_alias('RemoteSeleniumTestCase', 'DynamicSeleniumTestCase');
        } else {
            require_once 'LocalSeleniumTestCase.php';
            class_alias('LocalSeleniumTestCase', 'DynamicSeleniumTestCase');
        }
    }
}, true, true);

Then, to make your test suite run against your local Selenium setup and local browser, just do

export RUN_SELENIUM_REMOTE=0

before firing off the build, and to run the same thing against the SauceLabs server, do

export RUN_SELENIUM_REMOTE=1

Genius or insanity?! You decide.