Mocking Static Method Calls

When working with legacy code, my pet hate is to come across a Singleton, or a static method call that not only pulls out tons of dependencies, but also assumes that resources are out there, running.

The typical case I have been struggling with these past few months is “unit tests” assuming the application server is running to retrieve some EJBs. Yes, that’s right: unit tests that require the application server to run, with the database connections and all available. Not only you’re dragging the whole application to run to simply test one method, but it also takes far far too long. As it also requires the data sources to be available, you may also end up playing with test data in an unknown state and therefore get unpredictable results. In short, far from ideal from a unit testing point of view.

So you’re thinking, ok, I’ll mock the singleton/static method call, everything will be hunky-dory. But a singleton cannot be subclassed (the default constructor is private), and you cannot use polymorphism to mock a static method call. I had tried PowerMock to overcome this issue, but I have run into issues with JUnit 3, and couldn’t get it to work.

And today, after some search came the light at the end of the tunnel: jmockit.

Say you have a class BigUglyHelper which provides services as static methods:
notextile.

public class BigUglyHelper {
  public static String doStuff() {
    // Call an EJB session that calls an Entity bean, or Hibernate,
    // do fun stuff like paging or sorting, and return a String.
   return "BigUglyHelper";
  }
}

You are writing the class ClassUnderTest which, unfortunately, uses BigUglyHelper:

public class ClassUnderTest {
  public String getMyStuff() {
     String text = BigUglyHelper.doStuff();
     // do fancy things with text
     return text;
  }
}

Now, the unit test would be straightforward:

public class TestClassUnderTest extends TestCase {
  public void testGetMyStuff() {
    ClassUnderTest c = new ClassUnderTest();
    assertNotNull(c);
    // do other interesting asserts with c
  }
}

Straightforward? Nah. Remember, you need to have Weblogic (or WebSphere, or JBoss or…) running, you have to have your app deployed, you have to have proper data in your database, etc. Tsss.

So jmockit comes to the rescue, and that’s cool stuff. First, the mock for our class with the static methods:

public class SweetLittleMock {
        // Same signature as static method in real class
        public static String doStuff() {
            return "SweetLittleMock";
    }
}
And then, the test case.
public class TestClassUnderTest extends TestCase {

   @Override
    public void setUp() {
        Mockit.redefineMethods(BigUglyHelper.class, SweetLittleMock.class);
    }

   public void testGetMyStuff() {
        ClassUnderTest c = new ClassUnderTest();
        assertNotNull(c);
        assertEquals("SweetLittleMock", c.getMyStuff());
    }
}
 

The astounding thing is that I only have jmockit and JUnit in the classpath, so there is no jar dependency headache, and that’s for me a big big plus.

Singleton Testing

For testing singletons, it is not as simple, as getInstance() returns a reference to the Singleton. You have to use expectations and the cool thing about them is that they behave just the way you’d want a mocking framework to.

So, here is our nasty Singleton:

public class Singleton {
    private static Singleton instance;

   private Singleton() {
    }

   public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

   public String superService() {
        return "Singleton";
    }
}

with its superService() (so super it can only be provided by a single instance!!). This singleton is used by MyNeatClass that we’re trying to unit test:

public class MyNeatClass {

   public String doStuff() {
        return Singleton.getInstance().superService();
    }
}

There’s not much to it, but the key thing is that the Singleton is drawing unwanted resources into the unit test, and we don’t want that. So here is out test class (note that it extends JMockitTestCase since we are using JUnit 3. Annotations are the way to go if you’re on JUnit 4):

public class TestMyNeatClass extends JMockitTestCase {

    public void testDoStuff() {
        new Expectations() {

            Singleton singleton;

            {
                Singleton.getInstance();
                returns(singleton);
                singleton.superService();
                returns(“MockSingleton”);
            }
        };
        MyNeatClass neatClass = new MyNeatClass();
        assertNotNull(neatClass.doStuff());
        assertEquals(“MockSingleton”, neatClass.doStuff());
    }
}

The important bit is the Expectations: it is defined as an anonymous inner class. It defines a mock field (singleton), and an initialization block that records the method calls, and indicates what these method calls should return. For example, Singleton.getInstance(); will return singleton (return(singleton);).

jmockit definitely becomes a weapon of choice when dealing with legacy code where you are trying to bring in unit tests.

 
---

Comment

  1. Hi thanks a lot ! for the examples can you please mail me /show on page , the singleton example with Junit 4 using annotations

    Abhishek · 2012-09-27 19:00 · #

 
---