Tuesday, May 10, 2011

Testing untestable: JUnit + Mockito + Spring Test against static instances

This post is about my recent experience with the projects I thought don't exists anymore nowadays. Unreadable code base, chaotic design, any minor change breaks everything and, for sure, no tests. Worth mentioning deployment is nightmare. How this projects are supposed to evolve? I have no idea ... but we need to make fixes and changes. So ... what should we do? Write tests first!

That's not about how to write tests, but about techniques which allows to overcome some very bad coding practices in case you are not allowed to modify code base (as my team was put in such restrictions).

Let's start with such a pearl: private static initialized members (we will skip the thread safety aspects and concentrate on instance member only).
package org.example;

public class SomeStaticUtil {
    private static SomeStaticUtil instance = SomeStaticUtil.getInstance();
 
    public static SomeStaticUtil getInstance() {
        if( instance == null ) {
            instance = new SomeStaticUtil();
        }
  
        return instance;
    }
}
So how to substitute SomeStaticUtil with different implementation suitable for testing scenarios (aka mocks)? Remember, you are not allowed to modify the code (I would love to). There are few ways to do that:
- Excellent PowerMock framework. Didn't fit this project because bytecode manipulations crashed JVM.
- Magnificent AspectJ framework. Didn't fit this project because of complex aspects and necessary runtime weaving.
- Old and well-known reflection :-)

So what we can do here? Let's us exploit two excellent and very powerful test scaffolding frameworks: Mockito and Spring Test. Here is what we can do:
package org.example;

import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.test.util.ReflectionTestUtils;

public class SomeStaticUtilTestCase {
    private SomeStaticUtil someStaticUtil;
 
    @Before
    public void setUp() {
        someStaticUtil = Mockito.mock( SomeStaticUtil.class );
        ReflectionTestUtils.setField( someStaticUtil, "instance", someStaticUtil );
    }
 
    @Test
    public void someTest() {
        // ... some tests
    }
}
Very simple but powerful: replace private static member instance with mock implementation. Cool.

No comments: