I recently was working on code improvement and refactoring driven by unit testing when I came across the need to mock a Java static public method. This is something I had never done before, but given the emphasis on testing these days, how hard could it be? Well it took a number of hours to get working. Why so long? All examples I found showed the code but that is only 1/2 of what you need. The other half, which is even more important, are the Maven dependencies. The purpose of this article is to show a simple code example of mocking a Java public static method and the Maven dependencies you need to get it all working.
System Requirements
The code was developed and run using the following system setup. If yours is different, it may work but no guarantees.
- JDK 1.7.0_65
- NetBeans 8.0.1
- Maven 3.0.5 (bundled with NetBeans)
Maven POM dependencies
Listing 1 shows the Maven POM dependencies needed to run he unit test listed below. From my research, these are the only dependencies you need. NOTE that there is NO JUnit dependency and there is NO Mockito direct dependency. These are pulled in as transitive dependencies. If you do have them as direct dependencies, most likely you'll get at version mismatch and the unit tests will fail to run.
Listing 1: Maven POM dependencies
<dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>1.5.6</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-easymock</artifactId> <version>1.5.6</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito</artifactId> <version>1.5.6</version> <scope>test</scope> </dependency> <dependency> <groupId>org.easymock</groupId> <artifactId>easymock</artifactId> <version>3.2</version> <scope>test</scope> </dependency>
Class with a static method
Listing 2 shows the Java source code for a very simple class with a static method. The goal is to mock this method. Although this method is harmless and would not cause any problems in a unit test, most uses of static methods follow the builder design pattern and hence are responsible for constructing complex objects. In more real-world examples, the mock of the static method is needed to avoid the construction of the real object and instead return another mock object provided by the unit test.
Listing 2: Java HelloStatic class
/** * @author Michael Remijan mjremijan@yahoo.com @mjremijan */ public class HelloStatic { public static String getHello() { return "Hello"; } }
Class using a static method
Listing 3 shows the Java source code for a very simple class which uses a static method. Again the goal is to mock the static method call to return what we want.
Listing 3: Java GreetingInstance class
/** * @author Michael Remijan mjremijan@yahoo.com @mjremijan */ public class GreetingInstance { public String getGreeting(String name) { return String.format( "%s, %s" , HelloStatic.getHello(), name); } }
Mock the static method in a unit test
Listing 4 shows the Java source code for a very simple unit test which accomplishes the goal of mocking the static method so the unit test can control what the static method returns for asserting purposes.
When mocking a static method, you are going to be using a combination of PowerMock, EasyMock, and JUnit. Your unit test class may have other test methods testing other things with Mockito, so there is a lot of technology going on here. For sanity's sake, avoid using static imports! Static imports are a great convenience, but when mixing so many different technologies it get very hard to keep straight which methods are coming from which classes.
Listing 4: Java GreetingInstanceTest class
/** * @author Michael Remijan mjremijan@yahoo.com @mjremijan */ @RunWith(PowerMockRunner.class) @PrepareForTest(HelloStatic.class) public class GreetingInstanceTest { @Test public void echo() { String shrubbery = "Shrubbery"; String expected = "Shrubbery, Rita Red"; String actual = null; PowerMock.mockStatic(HelloStatic.class); EasyMock.expect(HelloStatic.getHello()).andReturn(shrubbery); PowerMock.replay(HelloStatic.class); { actual = new GreetingInstance().getGreeting("Rita Red"); } PowerMock.verify(HelloStatic.class); Assert.assertEquals(expected, actual); } }
Let's take a look at this code in a bit more detail.
#4 @RunWith
We need to run the test with the PowerMockRunner, not the normal JUnit runner.
#5 @PrepareForTest
We need to prepare the class with the static methods before running the unit test
#15 PowerMock.mockStatic(. . .)
Use PowerMock to mock the static methods.
#16 EasyMock.expect(. . .)
Use EasyMock to change what the static method returns.
#17 PowerMock.replay(. . .)
This changes the PowerMock mode to replay, which is the mode PowerMock needs to be in in order to run tests.
#19 actual = . . .
Run you test! The static method should be mocked and return what you set it to return on line #16.
#21 PowerMock.verify(. . .)
This changes the PowerMock mode to verify, which is the mode PowerMock needs to be in in order to verify tests.
#22 Assert.equals(. . .)
Make sure the actual matches the expected where the expected is constructed by mocking the static method to return a different value.
Conclusion
That's it. Quick, easy, and to the point.
References
johan.ha...@gmail.com. (2010 February 4). MockStatic. Retrieved October 2014, from https://code.google.com/p/powermock/wiki/MockStatic
sachin grover. (2013, June 25). PowerMock:: [java.lang.IllegalStateException: no last call on a mock available]. Retrieved October 2014, from http://stackoverflow.com/questions/17293780/powermock-java-lang-illegalstateexception-no-last-call-on-a-mock-available
sachin grover. (2013, June 25). PowerMock:: [java.lang.IllegalStateException: no last call on a mock available]. Retrieved October 2014, from http://stackoverflow.com/questions/17293780/powermock-java-lang-illegalstateexception-no-last-call-on-a-mock-available
end
No comments:
Post a Comment