Wednesday, November 9, 2011

Continuous Integration Pt III - Working with PHPUnit and Selenium

The continuing adventures of Hollyii as I attempt to implement continuous integration at my job. I've got:


  • Ant (my build tool), with a very small, very basic, mostly useless build script just to make sure it goes
  • PHPUnit (unit testing), which is running happily on my local OS X laptop
  • A means of bootstrapping PHPUnit to integrate it into the Codeigniter application, along with very few (meaningless) tests
  • Selenium Server installed, which hasn't been put to the test at all, except to ascertain through ps auxwww that it's genuinely running

So, what's next? Well, I think the next thing is to set up the Selenium extension for PHPUnit. This, I'm happy to report, is very easy. Assuming you're using Pear and have installed PHPUnit through Pear, this is all you do:

sudo pear install phpunit/PHPUnit_Selenium

I need to 'sudo' due to my permissions, but you might not need to.

Everything went well for me, and it's installed. I can verify using

pear list -a

Yep. It's there. Good.

I'd like to run a basic test with Selenium. I'm going to be referring to the PHPUnit documentation, found here, and a Zend Dev Zone article, found here.

Before I can do that, it turns out that I need one more Pear package, Testing_Selenium. I've installed it thusly:

sudo pear install pear/Testing_Selenium-beta

Note the "-beta" on the end. This was necessary due to the fact that my preferred state for Pear packages is "stable," but this package default state is "alpha." Including the "-beta" resulted in my installing v0.4.3 (beta) not 0.4.4 (alpha).

I've copied and pasted a test right out of the PHPUnit manual. It looks like this:

<?php
require_once 'PHPUnit/Extensions/SeleniumTestCase.php';

class WebTest extends PHPUnit_Extensions_SeleniumTestCase
{
    protected $captureScreenshotOnFailure = TRUE;
    protected $screenshotPath = '/var/www/localhost/htdocs/screenshots';
    protected $screenshotUrl = 'http://localhost/screenshots';

    protected function setUp()
    {
        $this->setBrowser('*firefox');
        $this->setBrowserUrl('http://www.example.com/');
    }

    public function testTitle()
    {
        $this->open('http://www.example.com/');
        $this->assertTitle('Example WWW Page');
    }
}
?>

This, alas, did not work. It should have failed, because the HTML title property for www.example.com is not really "Example WWW Page." But my test skipped the test altogether. It comes back as "OK, but incomplete or skipped tests!" Not what I wanted.

I did a little searching in the PHPUnit documentation, and I've learned that skipped tests are often due to the environment not supporting the test you're trying to run. You can even mark things as skipped, if you determine through your code within the test that you don't have access to something. They're example was:

<?php
class DatabaseTest extends PHPUnit_Framework_TestCase
{
    protected function setUp()
    {
        if (!extension_loaded('mysqli')) {
            $this->markTestSkipped(
              'The MySQLi extension is not available.'
            );
        }
    }

    public function testConnection()
    {
        // ...
    }
}
?>

I'm not doing this in my code (that is, checking for loaded extensions, marking things as skipped), but the fact that I am getting a "skipped" message suggests that my Selenium set-up isn't quite right. So, now I need to figure out how to make sure Selenium is set up correctly.

After some sleuthing, I have discovered that my version of Selenium is perhaps too new for PHPUnit's extension. When I originally started this quest for continuous integration, I intended to use Selenium RC but found that it had be deprecated in favor of the new Selenium 2 (a.k.a. Selenium WebDriver), so I downloaded that one. (Who wants a deprecated application?) But that was not what I needed, so I am now backtracking out of it.

The first step was to download the older model from the seleniumhq.org site. The page the link directs you to has all of the available downloads, including all the versions of Selenium RC. I downloaded the newest version of that program. This gave me a zipped archive containing a slew of files. All I needed was the selenium-server.jar file. I moved it to the directory in which I like to keep jar archives.

Moving the file was all that I really needed to do to "install" it; jar files are executable, and all you really need to do to run them is invoke java and pass it the location of the jar archive. But I'm on a Mac, and I want to take advantage of the launchctl program to launch the file for me. I created a plist file (almost identical to the one in my previous post), stored it where the plist files go (usually ~/Library/LaunchAgents), and issued two commands to 1) load the plist file and 2) to start the executable:

launchctl load ~/Library/LaunchAgents/org.seleniumhq.selenium.plist
launchctl start org.seleniumhq.selenium

But it didn't work. It failed silently! I know this because I ran

ps auxwww | grep selenium

All I saw was my old version. Doh! I need to stop the old one and start the new. They're both listening on the same port, so the new one couldn't start. It would have been nice to have some feedback, but oh well.

launchctl unload ~/Library/LaunchAgents/org.nhabit.Selenium.plist

That stopped the old one. I ran the same previous two commands as before:

launchctl load ~/Library/LaunchAgents/org.seleniumhq.selenium.plist
launchctl start org.seleniumhq.selenium

And now I can try to run my test again.

Well, it still skipped my test, but I know that what I've done was not a waste of time (at least, I think it wasn't), because I really needed to install Selenium RC anyway. Now, it's just a matter of making sure I've got it set up properly.

Alright. I poked around some more, using mindfulness based stress reduction to keep from having an actual tantrum on the hard, tile floor. Here is a tip: use the '--verbose' option when running phpunit. Let me say it again: use '--verbose' to help debug your phpunit testing! For example:

phpunit --verbose mytest.php

I had been using it to debug other tests, and it didn't give me anything. But this time, it let me know that it was attempting to connect to Selenium on port 4444 and couldn't. Very useful! Upon inspection, I realized I was running Selenium on port 4443. A-ha! Shut the server down, changed the port number, restarted it, and presto! It works. Amazing. I can now die happy.

No comments:

Post a Comment