Elegantly mocking static methods with Mockito 3, or, an rspec-style "AroundEach" hook for junit

Elegantly mocking static methods with Mockito 3, or, an rspec-style "AroundEach" hook for junit

By devin, 17 June, 2021

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.

Plain text

  • No HTML tags allowed.
  • Web page addresses and email addresses turn into links automatically.
  • Lines and paragraphs break automatically.