I’m unit testing and refactoring a large code base with many utility libraries in PHP.
There’s many libraries like this, filled with convenience methods used all over the site. Most of these static libraries interact with the configuration files (via another static class). Here’s a good example:
class core_lang {
public static function set_timezone()
{
if(cfg::exists('time_zone')) {
putenv("TZ=".cfg::get('time_zone'));
}
}
}
Then, of course, there’s another layer of more specific libraries elsewhere calling core_lang:: set_timezone() inside another function.
That’s making these classes VERY hard to write unit tests for, at least in PHPUnit, since you can only mock… basically one level in.
I ordered the book Working Effectively with Legacy Code, but what are some strategies for starting to refactor and manage this type of code for testability?
The most important principle to reduce coupling is Dependency Injection. There are many methods to actually implement it, but the base concept is the same:
Don’t hardcode dependencies into your code, ask for them instead.
In your particular example, one method to do this right is the following:
You define an interface (let’s call it ExistenceChecker for now) which exposes a method called ‘exists()’. In production code, you create a class which actually implements the method (let’s call it ConcreteExistenceChecker), and you ask for an ExistenceChecker object in the constructor of core_lang. This way you can pass a stub object which implement this interface (but with a dead simple trivial implementation) while you unit test your code. From now on, you don’t have to depend on a concrete class, just an interface, which introduces far less coupling.
Let me demonstrate it with a bit of code:
Production code:
Test code:
You can read more about this concept here.