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?
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());
}
}
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.
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.
As already pointed out, JMockit can be used.
An example:
@Test
public void mockStaticAndFinalMethods(@Mocked LegacyService mock) {
new Expectations() {{
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
}};
}
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.
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.