Text

I've had this book on my reading list for a little while now and I got through it in a single sitting yesterday so I thought I'd chuck up a quick review for it.

The Grumpy Programmer (actual name Chris Hartjes) amusingly blogs and tweets all things PHP and particularly PHPUnit. When I saw he was publishing this book, I was curious to see how his strident style would stand up to the longer form. Pleasantly, it turns out.

Chris maintains his gruff voice while whirling through the ins and outs of using PHPUnit. I've been using PHPUnit for a long time now and I find that when I am really familiar with a tool, I tend to re/(over)use patterns that have served me well in the past. So while I found I was familiar with much of the material in the book, there are more than a few tidbits here that I picked up. I feel even the most grizzled PHPUnit veteran's testing regime will benefit from a read through.

The book seems aimed at the less experienced which I did find a little surprising given the title. When I think cookbook I tend to think of the weighty O'Reilly tomes. This book though is more like a lengthy tutorial than a cookbook in the O'Reilly style. As a tutorial goes though it excels. It is detailed without being turgid and covers all the major aspects of using PHPUnit that I would expect it to and then some. I found the chapter on Test Doubles (that is mocks, stubs and fakes) to be particularly excellent. The vocabulary surrounding these terms tends to get mixed up and consequently programmers often treat them as the same thing. That leads in my experience, at best, to confusion. And at worst, to poor tests that are difficult to maintain.

As a quick aside, the book is published by LeanPub, who ensure authors receive 90% of the proceeds from their work. I think this is a wonderful initiative. Writing, especially for a programmer is tremendously hard and I like the idea of those that attempt it, and do a good job, get appropriately rewarded for doing so.

So, back to the book. You find peppered throughout the introduction to PHPUnit subtle wisdoms that are hard to argue with. A simple and you would think obvious example, is that of always providing the final argument to assert statements with a description message. This message is displayed when the test fails, helping you quickly identify where the problem lies. Another: writing strictly encapsulated code that eschews static methods and class variables, is (well unsurprisingly) easier to test that code that is constantly mutating global state.

The book is quite short, coming in (at least in my pdf version) at 85 pages. I feel like there is sufficient scope for more content here. Especially for a 'cookbook'. I would have loved to have seen more on using Data Builders for example. The chapter on data providers is great, but I find you often need more fine grained control over your fixtures. Factories and data builders are a couple concepts that once learned, significantly reduce the friction of TDD.

I perhaps would also liked to have seen more in introduction to TDD itself, motivations for it, and perhaps a brief comparison between the two principle TDD xUnit styles. Specifically the Statist TDD and Mockist/London School TDD styles. The former being a test style mainly interested in setting up some state, running a behaviour and checking the end state matches what you expected. The Mockist approach is less interested in observing State and instead is more interested in the messages passed between objects (method calls between collaborators).

Overall I enjoyed the book, and it fills a much needed role in guiding budding PHP TDD practitioners in the use of the most mature tool we have available in PHP. I picked up a few neat new tricks and I suspect many PHP programmers will do the same.

You can buy it now at grumpy-phpunit.com

Text

There's a peculiar issue right now with PHPUnit where it will not respect php.ini arguments supplied to it on the commandline (i.e. supplying -d arguments).

This matters a lot when you want to use xdebug on a project that runs off a virtual machine, or even perhaps a remote server.

The typical pattern (when using PHPStorm in my case) to invoke a remote cli debugging session is to set an environment variable telling the IDE what server configuration to use, and to tell PHP what remote host to connect to.

$ PHP_IDE_CONFIG='serverName=mydevmachine.local' php -dxdebug.remote_host=192.168.0.1 myphpscript.php
  

Now this will work fine, however if we want to debug during a phpunit test normally you would do this

$ PHP_IDE_CONFIG='serverName=mydevmachine.local' phpunit -dxdebug.remote_host=192.168.0.1 -c phpunit.xml
  

Unfortunately this doesn't appear to work at the moment (version 3.7.9). If I use the xdebug test client, I can see xdebug trying to connect to the localhost, ignoring what I've told PHPUnit. I'll look into this a bit more later, but I suspect PHPUnit isn't passing on the php.ini settings in a timely fashion for xdebug to hook into.

The solution to this problem is to make use of ssh port forwarding. This works exactly the same for a virtual machine as it would for a remote host, which makes xdebugging on a production machine (hopefully only ever in an emergency!!!) much more straight forward (and less insecure).

$ ssh -R 9000:localhost:9000 myvm.local
  

This sets up myvm.local to forward all connections to its localhost on port 9000 to the remote client's port 9000. When xdebug goes to connect to localhost:9000, it ends up actually connecting to mydevmachine.local:9000.

It's a bit of a hack, but a time saving one. The other alternative is Vim and its xdebug plugin. This isn't a bad alternative. But once you've experienced the power of PHPStorm's debugging implementation it's hard to go back.

Text

In PHPUnit it's quite possible to completely mock a class that employs a fluent interface without too much heavy lifting.

  $mock = $this->getMock('Zend_Mail');
    $mock->expects($this->any())
              ->method(new PHPUnit_Framework_Constraint_IsAnything())
              ->will($this->returnSelf());
  

This has the effect of making any method call on your mock object return a reference to itself.

Text

PHP projects are becoming ever larger and with that size comes complexity that can be difficult to manage.

Typically a PHP project will start off small, some basic webpage views, maybe a few forms, and likely, some sort of search functionality. This is pretty basic and if things need to change, you can normally change it in place, directly on the web-server and without too much grief.

At some point though entropy takes its toll and incremental changes have so may unintended side-effects it's no longer feasible to safely make edits in-place. A quick hack directly on the web-server stops being a 'quick win', and more like a game of Russian Roulette.

This is where unit and integration testing comes to the fore. A safety net to protect us when we start changing an application. But often getting a test environment set-up and representative of the live system is a lot of work in itself, and the temptation to just give into the 'Inner Pig' is too great. That is, to not bother running any tests, and just cross fingers.

Jenkins makes this a lot easier by doing much of the heavy lifting of building and running tests. It will check that code complies with defined style conventions. It can also check for common coding smells (copy/paste, duplication, long methods, large classes, poor expressions) and it can run custom scripts depending on the success of unit and integration tests.

Essentially Jenkins ensures that any changes that go into source control, do not 'break the build' in a process known as Continuous Integration.

Installation is very easy. Visit the Jenkin's site, follow the instructions for your platform and you'll have a Jenkins's install running (by default) at http://localhost:8080. I have also written a small installation tutorial: Installing Jenkins on Ubuntu/Debian Systems.

To get Jenkins initially dressed up for PHP follow the jenkins-php.org site upto and including the setup the Pear packages and Jenkins plugins.

You may or may not run into trouble with these instructions. For the most part it worked fine for me, however I chose to use the cli-tool and the plugin repository was not initialised. To get up and running, I had to force the plugin list to refresh manually.

Jenkins plugin list

Manage Jenkins > Manage Plugins > Click the Advanced Tab

If you're like me and want git or svn scm access, you'll want to install these plugins as well, as they are not included in the list on the jenkins-php instruction page.

Once the PEAR packages and Jenkins Plugins are installed, you're now ready to start preparing your application for Continuous Integration.

The initial configuration can be quite terse, as you will need an initial ant build file, and sample configurations for PHP Code Sniffer and the other code analysis tools. Thankfully Sebastian Bergmann - author of PHPUnit and much of the Jenkins PHP suite of tools - has developed a project wizard utility to simplify these initial configuration steps.

Install the PHP Project Wizard from the PHPUnit channel:

$ sudo pear install phpunit/ppw
  

Once installed, you can change into your project dir and run it with a few arguments to setup your initial build environment.

$ ppw --name 'My Project' --source ./lib --tests ./tests
  

You can also specify arguments defining default rulesets for PHP Code Sniffer and PHP Mess Detector. Omitting these arguments sees ppw select some sane defaults for you.

Now install the Jenkins PHP Job Template:

$ cd path/to/jenkins/jobs
  $ git clone git://github.com/sebastianbergmann/php-jenkins-template.git php-template
  $ chown -R jenkins:nogroup php-template/
  $ curl http://localhost:8080/reload # set this to be the path:port to your jenkins server
  

Jenkins and your system are now ready to manage a PHP Project. Please see my tutorial on Setting up a PHP Project in Jenkins for how to setup your first project in Jenkins.