Dec
18
Mocking static methods
Filed Under Coding |
A small introduction:
I've been writing a small piece of code recently. Since I'm test-infected, I cannot sleep well unless I've got test cases for everything I write. And I do use mock objects a lot - I test all my methods both in isolation.
Sometimes writing a piece of functionality as a static method is the right way to go. However, if I want to test a method that uses the static one, I can't isolate it! I was almost sick because of that breach in my solid testing strategy! I just had to solve it!
So, let's consider a simple static function:
public class Library { static public Map<String, String> getFileteredEnv() { Map<String, String> env = System.getenv(); Map<String, String> retenv = new HashMap<String, String>(); Set<String> keys = env.keySet(); for(String key : keys) { if (key.length() < 7) { retenv.put(key, env.get(key)); } } return retenv; } }
All it does is returning a list of environment variables which names have less than 7 characters. (Hey, it's just an example!)
So, there is a function which actually uses it. For the same of example, it's equally stupid:
public class ImportantClass { public int getSizeOfShortEnvironmentVariables() { Map<String, String> envs = Library.getFileteredEnv(); return envs.size(); } }
So, since the static function relies on the current state of the environment on the machine I run the test, it seems I can't reliably test the non-static one. A mock is not just a whim. I'd say it's a necessity!
At first, I was thinking about hacking the class loader to intercept the request to load Library class and return a mock instead. Fortunately I invented the solution when I was driving and I couldn't code it right away. A couple hours later I sat in front of my computer and started with Google (instead of launching Eclipse right away).
What I found was just plain incredible. Watch this:
class MockupLibrary { static public Map<String, String> getFileteredEnv() { Map<String, String> map = new HashMap<String, String>(); map.put("Test", "ABC"); map.put("Else", "def"); return map; } }
This is a mock version of the Library. It returns a controlled list of variables. Now, how do I put it in action?
@Before public void setUp() { Mockit.redefineMethods(Library.class, MockupLibrary.class); }
Isn't it cool? Thanks to JMockit (and java.lang.instrument), it is that simple! The simplicity and sheer coolness made me a fun of that library right away. A core of JMockit consists of a single class and a handful of methods. They do most of the job. And there are some nice features build upon it (like annotations).
And I guess I have to take a closer look at j.l.instrument. I just loooove this kinds of programming magic!
And, oh, BTW, you can redefine methods in a final class as well!
Comments
1 Comment so far
[…] of untestability Posted December 18, 2007 Tomo writes how to mock static methods and how cool is JMockit. This gives me an opportunity to write about […]