Earlier this week I attended the July meeting of the San Francisco PHP Meetup Group. ”But you’ve never written a line of PHP code!” you gasp. Well, technically yes, that’s true. I joined the group as an exercise in platform cross-pollination– as a Java developer, I’ve learned loads by programming in Ruby. Might the same be true of PHP?
After attending two meetups, it’s a split verdict: The first meetup I attended was an excellent presentation about Drizzle, the heir apparent to MySQL. Not at all PHP-specific, but still very useful (maybe even more so because it wasn’t PHP-specific). Monday’s presentation, given by PHPUnit creator Stefan Bergmann, was a bit of a disappointment. In truth, it was two presentations rolled into one: the first half an impromptu introduction to PHPUnit itself, and the second half billed as “Best Practices with Unit Testing”.
The PHPUnit introduction was predictable in some ways and surprising in others. Predictable sounds like a bad thing, but I mean it as a compliment: PHPUnit fits very well into the xUnit family, and if you’ve used (for instance) jUnit before, you’ll feel right at home using PHPUnit. For example:
- test method names always start with the word “test”
- by convention, test classes are the named and organized the same as the class being tested, but suffixed with “Test”
- running from the command line gives an execution report that looks a lot like jUnit’s
PHPUnit even improves on jUnit in a couple of areas: it has code coverage built in to the test runner, and if you pass the –colors flag, you can get a green or red bar right in the terminal! Pretty cool, huh?
Not everything is sunshine and lollipops, though. I have a conviction that frameworks and libraries should encourage the developer to Do The Right Thing, and by that measure, PHPUnit fares poorly. It contains a number of features that exist solely to enable what could charitably be called “code smells“. For example, by default PHPUnit will back up all global and static variables before test execution and restore them afterwards. Sure, it’s easy enough to turn this “feature” off. I still don’t like the idea of a tool that will both allow me to and assume that I will do something bad (i.e. use global variables) without so much as a warning message.
Also worrying was Stefan’s brief discussion of features he’s planning to add in the future, including the ability to test private methods. Hmmmm… from my perspective, the best way to implement that would be to print out the following message:
You have attempted to test a private method. Maybe the behavior in this method should be pulled into a separate class?
Stefan also talked quite a bit about another recent feature of PHPUnit–the ability to declare dependencies between tests. If you ask me, that’s a perfect example of the kind of thing libraries like PHPUnit should discourage users from doing. There’s enough material there for a separate post, but I’ll leave it at that for now.
To wrap up the introduction to PHPUnit, Stefan gave a quick overview of language/library-agnostic tips on unit testing such as ”minimize and inject your dependencies”, “keep methods small and simple”, and “don’t put business logic in the constructor”. Nothing new for anyone who’s written more than a few tests, but worth repeating for those who haven’t.
The title of the second presentation (“Best Practices with Unit Testing”) implied a fairly general view of unit testing, but was in fact completely specific to PHPUnit. Stefan’s first recommendation was to “use an XML(1) configuration file” for the test runner. Sounds reasonable enough, but the next recommendation was to turn off all of those features I complained about above. On one hand, it’s gratifying to see that the library’s creator feels the same way I do, but on the other hand, why aren’t those features turned off by default?
Another example of defaults gone awry is the requirement for a code coverage “whitelist”. By default, the test runner will include only the production source files encountered during the course of test execution in the code coverage results. That results in coverage numbers that are unjustifiably optimistic, because any files that have 0% coverage will be excluded! I’m sure there are (rare) circumstances that would require specifying this explicitly, but it seems to me like most of the time, PHPUnit should be clever enough to figure out how to Do The Right Thing without requiring user intervention.
I came away from the talk with the impression that the PHP community is a bit behind the Java community when it comes to unit testing practices. Obviously this judgement is completely unfair, since I haven’t looked at many PHP codebases, and PHPUnit isn’t the only unit testing framework for PHP. There’s definitely reason for optimism, though. PHPSpec, a Behavior Driven Design framework inspired by RSpec, looks like a pretty good example of someone striving to bring some fresh thinking from Ruby back to PHP. An example of platform cross-pollination at its finest, if you ask me.
(1)Ugh. Why XML? Why not PHP?