tags:

views:

621

answers:

6

I'm trying to implement unit testing for aproject, it uses a legacy "utility" project that is littered with static methods and many of the classes are final or their methods are final. I'm not able to update the legacy project at all.

JMock and EasyMock both choke on final methods, and I don't see a nice way to test the static calls. What techniques are there to test these?

+7  A: 

If you're able to refactor your code, you can wrap your calls to the final/static methods in simple instance methods, for example:

protected Foo doBar(String name) {
    return Utility.doBar(name);
}

This allows you to override your wrapper method in the unit test to return a mock instance of Foo.

Alternatively you can use Powermock, which extends Easymock (and Mockito) to allow mocking of final and static methods:

PowerMock is a framework that extend other mock libraries such as EasyMock with more powerful capabilities. PowerMock uses a custom classloader and bytecode manipulation to enable mocking of static methods, constructors, final classes and methods, private methods, removal of static initializers and more.

Here's an example test mocking a static final method, the example shows how to mock some other types too:

@Test
public void testMockStaticFinal() throws Exception {
    mockStatic(StaticService.class);
    String expected = "Hello altered World";
    expect(StaticService.sayFinal("hello")).andReturn("Hello altered World");
    replay(StaticService.class);

    String actual = StaticService.sayFinal("hello");

    verify(StaticService.class);
    assertEquals("Expected and actual did not match", expected, actual);

    // Singleton still be mocked by now.
    try {
        StaticService.sayFinal("world");
            fail("Should throw AssertionError!");
    } catch (AssertionError e) {
        assertEquals("\n  Unexpected method call sayFinal(\"world\"):", 
            e.getMessage());
    }
}
Rich Seller
+1 for PowerMock since refactoring is out of the question.
Matthew Murdoch
this looks like what I need, fo you know if it works in a Maven build?
talk to frank
I've not tried it yet, but it appears so: http://code.google.com/p/powermock/wiki/EasyMock_maven
Rich Seller
thanks, I'll try that
talk to frank
+1 for introducing a testing tool that I didn't know about. :)
tim_wonil
+2  A: 

How about a level of indirection / Dependency Injection?

Since the legacy utility project is your dependency, create an interface to separate it out from your code. Now your real/production implementation of this interface delegates to the legacy utility methods.

public LegacyActions : ILegacyActions
{
  public void SomeMethod() { // delegates to final/static legacy utility method }
}

For your tests, you can create a mock of this interface and avoid interacting with the legacy utility thingie.

Gishu
+1  A: 

If your non-refactorable method uses something like JNDI to connect to another service, I'd consider starting a JDNI service and populating it with stubs which you control. It's a pain but relatively straightforward. It may mean setting up a database or JMS listener or whatever but there should be a lightweight java implementation you can drop into the tests.

Mr. Shiny and New
That's ok for integration tests, but unit tests shouldn't need a database
Rich Seller
You may be able to mock the database, and inject that mock in the JNDI.
extraneon
@Rich Seller: I agree but if you had some eggs you could have bacon and eggs, if you had some bacon. By that I mean that the original question stated that he couldn't refactor the code and wanted to add tests. Sometimes you have to work with what you have, not what you want.
Mr. Shiny and New
+3  A: 

JMockit allows you to mock static methods and final classes. I assume it uses some classloadin-fu, although I haven't really looked into it.

JMockit Expectations API allows expectations to be set on any kind of method invocation (on interfaces, abstract classes, concrete final or non final classes, and on static methods), as well as on class instantiation through any constructors.

skaffman
A: 

As already pointed out, JMockit can be used. An example:

@Test
public void mockStaticAndFinalMethods(final LegacyService mock)
{
    new NonStrictExpectations()
    {
      {
        LegacyService.staticMethod("hello"); result = "Hello altered World";
      }
    };

    String actual = LegacyService.staticMethod("hello");
    new LegacyService().finalMethod(123, "test");

    assertEquals("Hello altered World", actual);

    new Verifications()
    {
      {
        mock.finalMethod(123, "test"); // verify this call occurred at least once
      }
    };
}

The JMockit project home page contains a comparison with PowerMock, for those interested.

Rogerio
A: 

JMock together with JDave can mock final methods and classes, if you need to. Here are instructions. That being said I would treat this legacy code (as others have suggested already) as an external dependency and build interfaces and mock those. It is another layer of indirection, but since you can't change that legacy code, it seems to be a reasonable one.

Yishai