In company where I work we have lots of "utility" classes, each has lots of code inside (thousands of lines), and they are all static. And one static methods call anothers. The problem here is that when you want to test other classes you have lots of pain when tested function uses these static utility classes inside because you can not override them(as they are static). We are using mockito for testing. Powermock is not an option because it breaks bytecode sometimes and it's much slower than mockito.
What we decided - is to move all them to singletons, step by step, so when testing we can mock singleton instance and test method we want (or even test some utility method because they also need to be covered by autotests). It's simple to do that for small classes, but in our case there are 6006 lines class, 1261 lines class, 2231 lines class and so on. Converting them all into singletons manually is a lot of pain, so is there any way to do that automatically? Or maybe some helpers at least.
Example of untestable code:
static Object methodA()
{
*very heavy code here*
}
static void methodB()
{
*some code*
xx = methodA();
*here is code I've changed and want to test*
*some code*
}
Let's imagine I changed something in methodB and want to test my change. So I don't want this methodA to be called as it's not related to the test. But I can't change what methodA does with mockito in such case. However it is possible if inside methodA there is a call to the same singleton method that can be mocked because it is not static.
So if you convert it to something like this
public class A {
private A(){}
public Object methodAInner()
{
*heavy code here*
}
public void methodBInner()
{
*some code*
xx = methodA();
*here is code I've changed and want to test*
*some code*
}
private static A getInstance()
{
return SpringUtils.getBean(A.class); // here can be anything that supports testing
}
static Object methodA()
{
return getInstance().methodAInner();
}
static void methodB()
{
getInstance().methodBInner();
}
}
You can make these SpringUtils return mocked object with real methodB and make almost no changes to existing code(useful for git blame), just remove static modifier and chnge methods names. An what is the most important for legacy - it allows not to change code that uses this utility class.