Last week, I had a discussion with Ionut – my colleague at Ninespices – about a piece of code I had wrote in one of our applications. The piece was meant to log the current user’s actions to the database for future reference. It looked something like this (more or less, the actual code isn’t that important):
protected function _log($info) {
$identity = Zend_Auth::getInstance()->getIdentity();
$this->save(
array(
'user_id' => $identity->id,
'info' => $info,
)
);
}
Ionut said to me: “I don’t like it! It’s hardcoded!”. I said that it’s not, as I don’t hardcode anything, since we all know that each time one hardcodes something in a software application, God kills a kitten.
But Ionut asked me a simple question: “How do you test it? How do you mock Zend_Auth?”. For the record, Ionut is the Test Driven Development guy of the team. He always writes tests for his applications, whereas I don’t, since my code “just works”…well, kind of…sometimes
. The point he made is interesting: how do you mock Zend_Auth? The answer is obvious: D’oh, you don’t!
In order to be able to properly use unit testing to test a piece of code, that code must be written in a manner that allows unit testing. To make a method “testable”, you must not instantiate other objects, call other classes’ static methods and so on. For instance, let’s consider a piece of code that looks like this:
public function methodThatRequiresTesting($input) {
// get data on the current logged user
$identity = Zend_Auth::getInstance()->getIdentity();
$userId = $identity->id;
// instantiate a helper class
$helper = new MyLibrary_Helpers_Foo();
// do whatever
}
It’s quite obvious that this code isn’t “unit testing friendly” as you cannot mock the helper or Zend_Auth. The proper way to write it in order to be “unit-testable” is either:
public function methodThatRequiresTesting($input, $userId, MyLibrary_Helpers_Foo $helper) {
// do whatever
}
…or…
public function methodThatRequiresTesting($input) {
// get the user id
$userId = Zend_Registry::get('userId');
// get the helper
$helper = Zend_Registry::get('Helpers/Foo');
// do whatever
}
This way, one can easily add mocks instead of the actual objects, isolate the functionality of this particular method and test it. Of course, the first “testable” code is wrong, as it breaks separation of concerns. A method should have input and output, and any additional entities (functions, classes) used inside the method’s body in order to compute the output from the input should be encapsulated within the method. Receiving these entities as input is a very bad practice, for obvious reasons.
The second code example is better from an architectural point of view, but it can be quite memory intensive, as one might instantiate a lot of helpers and place them in the registry without ever using them, thus wasting valuable system resources.
That very night, Ionut & I went to the 26th edition of Wurbe, where one the the topics was – beside the crappy beer – Test Driven Development. There was a somewhat heated debate on whether to use TDD or not, unit testing, functional (system) testing and so on. I particularly liked one of the opinions expressed there – by a guy who’s name I forgot
– on functional testing. There’s no need to write unit tests for all your components. Only write tests for those components which contain complicated algorithms, so each time you refactor the code and optimise the algorithm, you know if what you did is good or not. And write a system test, in the end, to test the whole application.
It makes more sense to me this way.