I've read most SO related questions ( here, here and there). The last question proposes four alternatives to make code which calls static methods unit-testable. I want to ask about my particular case: We have a "business logic layer" or "rules" project which contains 45 static classes (no state, just static methods). Moreover, they are not easily testable by themselves: most of them access the database and file system. It's not that bad, anyway: to access the database, they use the unique instance of some Mapper class (all Mappers are Singletons). Whenever I try to unit test something, I run into this wall. The biggest problem is that this is very, very important code, and changes to it should be planned very carefully. My question: How should I go about making this more unit testable? Should I write 45 interfaces and use dependency injection? Even so, how do I stub/mock Mappers?
PS: I've been reading Michael Feathers' "Working with Legacy Code", so direct references are welcome (other books too :)
Edit: Since some people said solutions might be platform-dependent, I'm working on .NET (C# and some VB.NET)
All you need to do is wrap the static method call inside an instance method and then use dependency injection to inject an instance of the wrapper class to the class under test.
Static methods are the methods in Java that can be called without creating an object of class. They are referenced by the class name itself or reference to the Object of that class.
The "this" keyword is used as a reference to an instance. Since the static methods doesn't have (belong to) any instance you cannot use the "this" reference within a static method.
The current situation is probably that no one dares to change anything in the code because it might break in unexpected ways. Make sure that everyone understands that you are improving the situation: Your changes might break the code but unlike before, those breakages will be found and once they have been found, they will be fixed forever.
That said, the next step depends on your experience and your credit with the team. If you want to play safe, use code like this (Java syntax):
Mapper {
public static Mapper INSTANCE = new Mapper(); // NEW code
protected void doImpl() { // NEW CODE
... code copied from impl()... // OLD code, NEW PLACE
}
public static void impl() { // OLD code
INSTANCE.doImpl(); // NEW code
}
// OLD code ...
}
This is a very simple change which allows you to overwrite INSTANCE
from your tests. For production code, you don't do anything and the default will make the code behave exactly like before.
That way, you can replace one method at a time. You can stop following this path at any time - each change takes only a couple of minutes and it's a refactoring: The code does exactly what it did before. Since each change is so small and can't break anything, you can replace one method, write all the unit tests that you couldn't write before, rinse, repeat. Lastly, if you don't want/need to rework all static methods, this approach gives you all the leeway you could ask for.
In a second step, you can introduce DI or any other technology which will make you happy. The advantage of this approach: When you come to the complex changes, you will already have unit tests that'll protect you.
If you started with DI, you'd have to change a lot of code in all kinds of places - without proper unit tests that could protect you.
Make the interface of the Mapper class and replace the mapper functionality with a new moc implementation. I think that you will need to implement the abstract factory which will generate the instances of Mappers. So make an IMapperFactory and two implementations of this DBMapperFactory and MocMapperFactory pass the instance of this object to your code where you access the Mappers and generate these with using this instance.
Gl.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With