Writing testable code is tricky. On reason is because you have to test all of your software components individually and isolated from each other. Imagine that you’re testing your new and cool DB abstraction layer. It’s using:
- Some sort of error log
- A config component to fetch DB credentials
- Your caching layer to temporarely save stuff
- I almost forgot: It’s using PDO
Having everything tied up like this, you’re going to have a hard time testing it. After all, you don’t want to test the connected components, just your new and cool query class. That’s where Dependency Injection kicks in – along with some other patterns with cool names. The goal is to break your piece of software down to a bunch loosely coupled components and tie them together dynamically. As soon as your software is getting the Lego look and feel, you know that you’re on the right track. This is where you can use Dependency Injection for the logging component:
// Instantiating the components
$loggingComponent = new Fancy_Error_Log();
$fancyQuery = new Fancy_Db_Query();
// Tell your query what to use for logging
Does it feel Lego already? Then go on and do the same with the other 3 dependencies.
Now, when it comes to testing, you can easily replace your logging component by a mock. That’s a fake component which acts like your usual logger. Instead of logging, it does something else, like routing everything to /dev/nulll or, even better, recording any logging attempts. That way you have a tool to check if your query class is logging the right stuff. Martin Fowler wrote a great article “mocks aren’t stubs” that explains the difference between dummy, fake, stub and mock components, if you care.
So here’s an example of how to set up your Query class with a mock logger:
$fakeLogger = new Fancy_Error_LogMock();
Pretty cool, eh? But not completely DRY yet. Don’t feel like writing mock objects all day long? Most testing frameworks offer default mock implementations (aka “self-shunting”) to make your life even easier. Let’s try that with a fictional example with PHPUnit3’s mock object implementation. With it’s fluent interfaces, it’s a perfect match for our Query class in terms of fancyness!
// Setting up the mock object
$logMock = $this->getMock(‘Fancy_Error_Log’, array(‘write’));
// We expect our query to call Fancy_Error_Log::write(‘Query Fail’!);
// Make the query fail miserably
$fancyQuery->select(‘SELECT FAILcolumn FROM FAILtable’);
Is this all?
Yes, it is. For now. When searching for the my headline in Google, all I got was search results for Java, C++ and .NET. I mean, come on!
Probably most of you knew the way of testable software design already. Probably, you did so but didn’t know how to name it (which only matters for management, anyway). However, holding up the PHP flag is not my sole intention. Let’s see this as a little introduction of what I originally wanted to write about: Untestable Software. Often, there are situations where writing testable code is killing your performance or usability or both. So there will be a follow-up post. Hopefully sooner than later.