My team is currently migrating one of our Java codebases from junit 4 to 5, and with it we lost two of our favourite features: mocking static methods with PowerMock, and Theories. I'll leave Theories alone in this post so I can focus on PowerMock.
PowerMock has served us well but has some drawbacks, mainly around its slow performance and arcane errors due to the way PowerMock changes your classloading.
Mockito 3 luckily has a nice new feature that allows us to replace PowerMock before migrating each test to junit 5: https://javadoc.io/static/org.mockito/mockito-core/3.11.1/org/mockito/M…. Riffing off of their example, it looks something like this:
@Test public void my_test() { try (MockedStatic m = mockStatic(Foo.class)) { when(Foo.doSomething(withSomeParam)).thenReturn("bar"); // test some code that depends on Foo } }
This is all well and good but my tests used to look more like this:
@Before public void setup() { PowerMockito.mockStatic(Foo.class); PowerMockito.when(Foo.doSomething(withSomeParam)).thenReturn("bar"); } @Test public void my_test() { // test some code that depends on Foo }
A lot of my tests become much less readable if I use the style from the Mockito website. I can make this a bit easier by using a lambda:
@Test public void my_test() { mockFoo(() -> { // test some code that depends on Foo }); } private static void mockFoo(CheckedRunnable test) { try (MockedStatic<Foo> m = Mockito.mockStatic(Foo.class)) { when(Foo.doSomething(withSomeParam)).thenReturn("bar"); test.run(); } }
But this is a lot of work and still makes the test method somewhat busier.
My new favourite approach is using a Junit Rule. This has apparently been available since 4.7 and although it's a bit of annotation magic, I think it's fairly easy to follow and makes the tests much more readable:
@Rule TestRule mockFooRule = (RunWithCloseableRule) () -> Mockito.mockStatic(Foo.class); @Before public void setup() { when(Foo.doSomething(withSomeParam)).thenReturn("bar"); } @Test public void my_test() { // test some code that depends on Foo }
This depends on creating this little gem to make the above code compile:
@FunctionalInterface public interface RunWithCloseableRule extends TestRule { AutoCloseable setup(); @Override @SneakyThrows default Statement apply(Statement base, Description description) { return new Statement() { AutoCloseable c = setup(); base.evaluate(); c.close(); }; } }
Of course, the lambda approach is going to be better in some cases to be more explicit, and in many other cases I can use dependency injection in the subject under test to avoid needing to mock static entirely. But still! It's nice to have these tools in the toolbox to craft code that's sublimely readable.