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:
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!
That's just semantics :-)
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
@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.
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.
That looks really cool Jan! It would really help with refactoring!
Post a Comment