Monday, November 21, 2011

Applying Mathematics To Web Design - Smashing Magazine

It's not Yii. Heck, it's not even PHP, but it's dang cool:

Applying Mathematics To Web Design - Smashing Magazine

Thursday, November 17, 2011

Firefox4 on OSX 10.5 (leopard) failing to open from command line : bang_head_on_wall

I needed to launch Firefox from the command line in OS X 10.5.8, and it just wouldn't work. This blog post saved me:

Firefox4 on OSX 10.5 (leopard) failing to open from command line : bang_head_on_wall

So very easy to fix!

Tuesday, November 15, 2011

Selenium 2 from PHP code | Web Builder Zone

This article breaks down the current array of choices for PHP and Selenium better than anything I've seen so far. Nice work!

Selenium 2 from PHP code | Web Builder Zone

Thursday, November 10, 2011

Continuous Integration Pt IV - Working with PHPUnit, Selenium, and SauceLabs

Ah, yes, the saga continues!

Today, I am going to try to get our PHP Selenium tests to run on SauceLabs' OnDemand service. SauceLabs is a cross-browser testing service. You can run your tests by hand - by opening an instance of a particular browser on their server via your own Web browser - or in a more automated fashion. I want to do the latter. What this entails is writing tests to tell the SauceLabs servers with which browser we wish to test on which operating system (e.g., IE7 on Windows). You can run the same test across multiple browsers, and any failed tests will be recorded for you to see exactly what went wrong. We're planning to automate the running of these tests, kicking them off when a developer pushes their code to the staging server.

The tests can be written in a number of languages for either Selenium RC or WebDriver. Because I'm using PHPUnit (since I code primarily in PHP), I'm stuck with Selenium RC, which is quite old but is supported by extensions in PHPUnit. I've successfully run tests on my local development machine, but now I'm going to adding one more layer of complexity, i.e. the SauceLabs servers. This requires one more set of extensions be added to the PHPUnit test framework. Here goes!

Install the SauceLabs SauceOnDemand packages from Pear:


sudo pear channel-update pear.php.net
sudo pear upgrade pear
sudo pear channel-discover pear.phpunit.de
sudo pear channel-update pear.phpunit.de
sudo pear channel-discover components.ez.no
sudo pear channel-update components.ez.no
sudo pear channel-discover pear.symfony-project.com
sudo pear channel-update pear.symfony-project.com
sudo pear channel-discover saucelabs.github.com/pear
sudo pear channel-update saucelabs.github.com/pear

No problems there. I'd already done the "discovering" of pear.phpunit.de and pear.symfony-project.com, so I only needed to channel-update for those.

Next, install the SauceLabs Selenium PHPUnit extensions:

sudo pear install -a saucelabs/PHPUnit_Selenium_SauceOnDemand

Great! That worked for me with no problems.

The next steps requires a SauceLabs account, I think. It was very cryptic, at first, because, without being logged in, it just says to do the following:


sauce configure


But that didn't do anything but throw a strange error and then tell me I was ready to run saucy tests and that I feel hot and saucy (which I didn't, btw).


After creating a free account (which anyone can do) and logging in, returning to the instructions page revealed the same instructions to do "sauce configure," but that command was followed immediately by my username and a hash code. So, I ran that. No errors, and now I really am feeling hot and saucy.


I'm going to run a test of a test. (I guess I'm feeling saucy and testy.)


curl -s https://saucelabs.com/example/se1/php/private-JJHyCGMF | bash

Well, this threw up a big ugly error with stacktrace and junk. It looks like something is calling an undefined method:

undefined method PHP_CodeCoverage_Filter::getInstance()

That's no good. I'm wondering if I need to update my version of PHPUnit is old? Maybe I should just upgrade. I did this

pear upgrade

and suddenly faced all kinds of ugly. Upgrade failed. Doh! Oh, I forgot to use 'sudo':

sudo pear upgrade

Much better! Now I've got upgraded Pear stuff.

I ran the curl command from above again, and got exactly the same results. Hmmm...

Looking at my Pear channels and packages, I've noticed that I have two versions of PHPUnit installed, one in the traditional pear.phpunit.de channel, and one under saucelabs.github.com/pear. the saucelabs version is older than the other one. There is also a different version of PHPUnit_Selenium in that channel, though that's not the one that is active. I've emailed the SauceLabs people for some help on this. My concern is that it's due to conflicts in one version vs. another, and I'd like to nip that possibility in the bud.

In the meantime, I'm going to look at the problem code. The error message is:

Call to undefined method PHPUnit_Util_Test::getParallelismSettings() in /opt/local/lib/php/PHPUnit/Extensions/SeleniumTestCase.php on line 352

I won't write about that in this post, however. For the time being, this is a work in progress.

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.

Continuous Integration Pt II - Unit Testing and Codeigniter

I'm still plugging away at getting our shop set up for continuous integration. We use Codeigniter for our application, and this presents a huge can of worms with regards to unit testing. First of all, Codeigniter does include some minimal unit testing support within the app, but, by all accounts, it's pretty darn small. The holy grail for unit testing in PHP, IMHO, is PHPUnit. It's pretty much the standard, and it's well supported. So, it makes sense to use that, since our tests will continue to be relevant, whether we change versions of Codeigniter, whether developers come and go, etc. It's a nice constant to shoot for.

The big problem is that PHPUnit doesn't just naturally hook into Codeigniter. Things get even more complicated when you start looking for solutions for bootstrapping it, as it greatly varies depending on the version of Codeigniter you're using. Ellis Labs (the developers of Codeigniter) is working on integrating PHPUnit into the framework at some point in the future, which will be version 2.?. In the meantime, another developer has started with their code and completed it to make it work, and it works with version 2.0.3. Well, we're on version 2.0.2. So, that's a bummer. The earlier attempts to integrate PHPUnit with Codeigniter are usually for 1.7.x and below.

In the end, I managed to get another developer's much simpler bootstrapping code to work. You can find it here. That's a Codeigniter forum post. Look for CarloGI's post about his technique. He includes a download of example files. Download the files, put the where they belong, and make sure you modify the _getDBObject function of the bootstrap.php file to match your mysqli settings for your app. You'll want to include the --stderr argument when you run phpunit so that you don't get the output buffers error. One other thing I had to do was to comment out an echo statement in myControllerTest.php. Otherwise, it will cause the output buffer error, as well.

Sorry these instructions are so cryptic. I wanted to get them out there (mostly for myself), but they might help someone else. If I have time, I will add more detail.

Thursday, November 3, 2011

Format My Source Code for Blogging

This is very useful for blogging source code.

Format My Source Code for Blogging

I'll go through and fix some previous posts soon.

Notes on Apache Ant

I'm an Apache Ant newbie. I've known for years that it exists, but I've never used it. I just installed it, today, and now I'm trying to understand what it does and how to use it. Here are some notes:


  • An Ant build contains one project
  • That project has at least one target
  • Each target contains tasks


The build file defines these things and is written in XML and is called build.xml. It has to define at least one target. The target defines one or more tasks to perform when it is run. Here is my first build file:

<?xml version="1.0" encoding="UTF-8"?>  
<project name="helloworld" default="init" basedir=".">  
    <description>  
    Build file for the Hello World application.  
    </description>  
    <target name="init" description="Initialize the build.">
        <touch file="testinit" />
    </target>
</project>

It includes a description (of the project) and one target. The target is simply a set of tasks you want to run. It can be called whatever you want, really. The target name attribute is what you will call from the command line when you want to run those tasks. In this example, my target has the name attribute "init". In that target, the only task I've included is the "touch" task. This will execute the *nix "touch" command. The "file" attribute within the "touch" task specifies which file to touch. (See the Ant manual for more available tasks. They are legion.)

Now, to run this, I go to the directory containing the build.xml file and issue the following command:

ant init

Notice that I didn't say "ant build.xml" or "ant build." This is because I'm telling ant to run a specific target. Also, if I change the name of the file from build.xml to moo.xml, it will break. Ant expects the build file to be called build.xml.

Wasn't that fun?

Continuous Integration Part I

I'm setting up a suite of continuous integration tools for my current job. We are a PHP shop using Mercurial for version control. My catalogue of tools is as follows (so far):

  • Ant (automated build tool)
  • PHPUnit (unit testing)
  • Selenium (functional testing)


I haven't decided on a CI server, yet. I'm looking at Hudson, as it has an extension to support Mercurial. Eventually, I'm hoping we'll use a service, like SauceLabs' Sauce OnDemand to perform our functional tests, since I'd like to take advantage of their cross-browser tests. They use Selenium RC, so we'll want to get started using that, ourselves. As of version 3, PHPUnit has support for writing tests for Selenium. I don't know, yet, whether those would work with SauceLabs, nor do I know whether we'll use those or write our scripts the traditional way, as per the documentation on the Selenium site. Another thing to note is that Selenium RC (Selenium 1) is officially deprecated in favor of Selenium Webdriver (Selenium 2), but they're going to support Selenium RC, doing bug fixes and the like, as it has more features than Webdriver (at least for now).

To get Ant to work with Mercurial, I'm going to need ANT4HG.

I still need to ascertain what all, exactly, will go into my build process. I want it to represent a complete rebuild of our entire Web app from scratch, complete with a set of clean test data and the application code, as well as all tests. I'm suspicious, however, that there will be more to it than doing a clean check out (in hg terms, a clone) of our code base and grabbing a db schema from somewhere, but we'll see.

Tasks done so far:

  1. I've installed PHPUnit using Pear (which I had already installed through Macports when I installed php-5). 
  2. I have installed Ant (an Apache project) using Macports.
  3. Installed ANT4HG as follows:
To install ANT4HG, I downloaded the binary from the SourceForge downloads. I extracted the zip file and ended up with a jar file. I then moved the jar file to the ant lib directory. On my system, that is located here: /opt/local/share/java/apache-ant/lib. It should now be available for Ant to use.


OK, now for Selenium (deep breath). I need the Selenium standalone server. Eventually, I won't need this (I hope), since we'll rely on SauceLabs for this service. But for the purposes of setting up a test of our chosen tools - and also because we're not going to subscribe to a service, yet - I need to have it up and running locally. I'm following instructions I found here, with the exception that I put my selenium jar file in /opt/local/share/java/selenium instead of /usr/lib/selenium. (Either way, I needed to create the selenium directory.)

I then created a launch file so that Selenium would be launched on system startup. The file is copied from the instructions I used on Dan Straw's site. Since my "selenium" directory is in a different location than his, I had to modify my launch file to reflect the proper location. I also had to change the name of the jar file, as he mentions in his write-up. I loaded the plist file and started the service. Using "ps auxwww | grep -i selenium" I learned that it's up and running. Good.

Everything is installed, now, so I just need to get my feet wet and play with it. I'll save that for another post. In the meantime, I'll be looking this.