{ by david linsin }

April 27, 2009

Using Reflection in Unit Tests II

A couple of days ago, I blogged about the pros and cons of using Reflection in unit tests. Thanks to Samuel, who gave me a pointer to "PowerMock", I don't have to deal with the Reflection API anymore.

PowerMock is a Java framework that allows you to unit test code normally regarded as untestable.


To be more precise, PowerMock takes away the annoyance of dealing with the Reflection API. In case of the example presented in my previous post, instead of this code:

@Test
public void test_Calculate_Quote() throws Exception {
Contract classUnderTest = new Contract(1L);
Method calcMethod = getMethodOfClass(Contract.class, "calculateQuote");
BigDecimal quote = (BigDecimal) calcMethod.invoke(classUnderTest, new DateMidnight(1982, 4, 27));

assertEquals(new BigDecimal("7.79"), quote.setScale(2, RoundingMode.FLOOR));
}

private Method getMethodOfClass(Class argClass, String argMethodName) {
Method[] methods = argClass.getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equals(argMethodName)) {
method.setAccessible(true);
return method;
}
}
throw new NoSuchMethodError("couldn't find " + argMethodName + " on class " + argClass);
}


you simply write this:

@Test
public void test_Calculate_Quote() throws Exception {
Contract classUnderTest = new Contract(1L);
BigDecimal quote = (BigDecimal) Whitebox.invokeMethod(classUnderTest, "calculateQuote", new DateMidnight(1982, 4, 27));
assertEquals(new BigDecimal("7.79"), quote.setScale(2, RoundingMode.FLOOR));
}


I like the approach of hiding the complexity of the Reflection API behind an abstraction and in case of Whitebox it's even named perfectly. But accessing private methods is not the only thing PowerMock lets you do. In addition to much more you can set private fields, invoke hidden constructors and mock static methods. A feature that I really like is mocking final classes or methods, it can be quite helpful, when you are dealing with JDK classes like java.io.File.

I think PowerMock is a great extention to EasyMock and it should go into every developer's tool box. It's definitely going straight into mine.

6 comments:

Ollie said...

Although I kinda dislike the Whitebox as nobody could really tell me how you could look more into a white box over a black box.

Glassbox would be more appropriate IMHO ;).

Despite that... great hint!

david said...

That's just semantics :-)

Jan Kronquist said...

Good to hear that you find PowerMock useful! Also note that PowerMock does some clever things that most of the time you don't even have to specify the method name and only give its arguments. The idea is to increase the possibility that the test survives a refactoring.

http://code.google.com/p/powermock/wiki/BypassEncapsulation

david said...

@Jan

that sounds great! Could you tell me how I can invoke a private method without specifying the name? I cannot figure it out looking at the API docs.

Jan Kronquist said...

Sorry, I didn't realize that this feature has not yet been released! This feature will be included in our next release within a few weeks. However, you can try it using the trunk. Checkout from subversion and do "mvn install"

Simply leave out the method name, for example:

double arg1 = 5;
String arg2 = "hello";
Whitebox.invokeMethod(someObject, arg1, arg2);

PowerMock will find a method that takes a double and String as arguments.

The same idea is available for fields, ie:

Whitebox.setInternalState(target, value)

will find a find in target where value can be assigned.

david said...

That looks really cool Jan! It would really help with refactoring!


com_channels

  • mail(dlinsin@gmail.com)
  • jabber(dlinsin@gmail.com)
  • skype(dlinsin)

recent_postings

loading...