by Johan Haleby - Java, Testing, Tips & Tricks
PowerMock is a Java framework that allows you to for example mock static and final methods, final classes and construction of new objects. PowerMock is thus a powerful framework and it’s quite easy to misuse it unless you know what you’re doing. What I’ve seen time and again is that people are using mocking when what they probably should be doing is stubbing. With all the powerful features in PowerMock it can easily lead to complicated and hard-to-maintain test code.
So what’s the difference and why does it matter? Mocking can be summarized more in terms of a specification:
1. Setup how your mocks should behave in your test. This means creating a specification for the mocks involved in this particular test.If a mocked method is called more or less times than what’s defined in the specification the test will fail. I would argue that most often you don’t need this kind of rigorous approach. Sure you may need a way to specify that a method (let’s call it X) doesn’t call a third-party system but instead having it return some pre-defined value. It doesn’t follow that it’s always interesting to verify that X was called a particular number of times with with some exact arguments. Sometimes it’s legitimate and useful, but as long as the result of the method we’re testing behaves as expected (for example that it returns the expected result), the call to X is less important and may be regarded as an implementation detail.
Stubbing on the other hand doesn’t require a specification to be fulfilled. What you do is to something like this:1. Define what the collaborator methods should return when you run your test
2. Perform the actual test
Now if a collaborator happens to be called more than once, or perhaps not at all, doesn’t really matter as long as the end result of the actual test behaves as expected.
So what does all this has to do with PowerMock? Well PowerMock has something called a “stubbing” API that let’s you do quite many things without having to revert to mocking. You don’t actually have to depend on any third-party mocking API either (like EasyMock and Mockito).
If all you need to do is stubbing a non-static public collaborator method you don’t need PowerMock. You’re probably better off with just vanilla Mockito or EasyMock. How ever if you want to stub a static or private method, suppressing a constructor or replacing a method PowerMock can help!
So first consider stubbing a method with PowerMock. Consider the following simple Java class:
|
1 2 3 4 5 6 7 8 9 10 11 |
public class MyClass1 { static String hello(String arg) { return "Hello " + arg; } private String goodbye(String arg) { return "Goodbye " + arg; } } |
Let’s say we want to stub the static hello method to always return an expected value using the PowerMock stubbing API:
|
1 |
stub(method(MyClass1.class, "hello")).toReturn("Hello World"); |
Which means that a call to the hello method like this:
|
1 |
MyClass1.hello("John Doe"); |
will now always return the string "Hello World".
stub is statically imported from org.powermock.api.support.membermodification.MemberModifier and method from org.powermock.api.support.membermodification.MemberMatcher. You would also have to prepare MyClass1 for test using @PrepareForTest(MyClass1.class) and use the PowerMock runner @RunWith(PowerMockRunner.class).
Stubbing a private method looks very similar:
|
1 |
stub(method(MyClass1.class, "goodbye")).toReturn("Something"); |
You can also suppress methods, constructors and fields that you’re not interested in. Essentially what this means is that “this piece of code doesn’t do anything useful for this particular test case, just get it out of the way”. For example consider the following class:
|
1 2 3 4 5 6 7 8 9 10 11 |
public class MyClass2 { MyClass2() { System.load("some.dll"); } static String hello(String arg) { return "Hello " + arg; } } |
When unit testing the made-up hello method there’s no need for us to load the “some.dll” so let’s get rid of it:
|
1 |
suppress(constructor(MyClass2.class)); |
It’s also possible to suppress all constructors of a class:
|
1 |
suppress(constructorsDeclaredIn(SomeClass.class)); |
or all methods:
|
1 |
suppress(methodsDeclaredIn(SomeClass.class)); |
In this case all methods declared in SomeClass will return a default value.
You can also essentially suppress an entire class, meaning all methods, constructors and fields:
|
1 |
suppress(everythingDeclaredIn(SomeClass.class)); |
What if we only want to stub the hello method in MyClass1 when the parameter arg is equal to “John”? For all other arguments we like to invoke the original method. We can achieve this by replacing the method with an InvocationHandler like this:
|
1 2 3 4 5 6 7 8 9 10 11 |
replace(method(MyClass1.class, "hello")).with( new InvocationHandler() { public Object invoke(Object object, Method method, Object[] arguments) throws Throwable { if (arguments[0].equals("John")) { return "Hello John, you are awesome!"; } else { return method.invoke(object, arguments); } } }); |
This means that if you invoke MyClass1.hello("Someone") it’ll return Hello Someone but calling it with John will return Hello John, you are awesome!.
You can also replace a method with another method! For example you may want to replace all log outputs in an integration test with your own method that simply prints everything to the console. Consider the following example:
|
1 2 3 4 5 6 7 8 |
public class MyClass3 { static String hello(String arg) { Logger.debug("Calling method hello with: "+arg); return "Hello " + arg; } } |
Imagine that the Logger.debug(..) method logs to disk but in our test we simply want to print to the console. We could implement a new class for this:
|
1 2 3 4 5 6 7 |
public class ConsoleLogger { public static void print(String arg) { System.out.println("Method called with arg: "+arg); } } |
And replace the Logger.debug(..) method with the ConsoleLogger.print(..) method in our test case:
|
1 2 |
replace(method(Logger.class, "debug")). with(method(ConsoleLogger.class, "print")); |
This means that all calls to Logger.debug(..) will be replaced with ConsoleLogger.print(..). Note that this only works for static methods!
(Also note that in a real-life scenario there are most likely better ways to solve this problem, e.g. by simply configuring the original Logger to print to the console during our integration test).
As you’ve hopefully seen there are more to PowerMock than just mocking. For example it’s often better to simply stub third party api calls than to mock them. PowerMock has good support for doing this in a simple way even for legacy systems. As always PowerMock should be used with care so whenever you find the urge to use PowerMock make sure you look into possible alternatives as well.
package com.test;
public class MyClass1 {
public static String hello = createName();
private static final createName() {
return “Goodbye ” ;
}
}
Here I need to mock static variable hello .
but I cannot use @SuppressStaticInitializationFor(“com.test.MyClass1″) to suppress whole class loading. Is there any other way to do it
Alltest.java:
@RunWith( Suite.class )
@SuiteClasses( {test1.class, test2.class} )
public class AllTests
{
@BeforeClass
public static void initTest() throws Exception
{
}
@AfterClass
public static void disposeTest()
{
}
}
test1.java
@RunWith( PowerMockRunner.class )
@PrepareOnlyThisForTest( {Object.class} )
public class test1 extends TestCase
{
@Test
public void testcase()
{
Shell sh = new Shell();
assertTrue( true );
}
}
test2.java
@RunWith( PowerMockRunner.class )
@PrepareOnlyThisForTest( {Object.class} )
public class test2 extends TestCase
{
@Test
public void testcase()
{
Shell sh = new Shell();
assertTrue( true );
}
}
when run alltest.java i get error:
java.lang.UnsatisfiedLinkError: Could not load SWT library. Reasons:
no swt-win32-3650 in java.library.path
no swt-win32 in java.library.path
Native Library C:\Users\w00207976\AppData\Local\Temp\swtlib-32\swt-win32-3650.dll already loaded in another classloader
Can’t load library: C:\Users\W00207~1\AppData\Local\Temp\swtlib-32\swt-win32.dll
ask:
i must use swt api,how do I deal with this problem