Why Doesn't Mockito Mock Static Methods

Why doesn't Mockito mock static methods?

I think the reason may be that mock object libraries typically create mocks by dynamically creating classes at runtime (using cglib). This means they either implement an interface at runtime (that's what EasyMock does if I'm not mistaken), or they inherit from the class to mock (that's what Mockito does if I'm not mistaken). Both approaches do not work for static members, since you can't override them using inheritance.

The only way to mock statics is to modify a class' byte code at runtime, which I suppose is a little more involved than inheritance.

That's my guess at it, for what it's worth...

Static mock failing on JDK 17

The difference stems from the fact that JDK 17 no longer calls Clock.systemUTC() in implementation of Instant.now()

Please compare:

JDK 17:

public static Instant now() {
return Clock.currentInstant();
}

JDK 15:

public static Instant now() {
return Clock.systemUTC().instant();
}

If you insist on mocking static methods, you could stub Instant.now(), not Clock.systemUTC() - thus you don't rely on the implementation of Instant.now()

As discussed in the comments in under your post, this is not the recommended approach - class Clock was designed specifically to make time-handling code easier to test, use a Clock in your code instead of calling Instant.now()

@Test
void mockStaticClock() {
final Instant instantExpected = Instant.parse("2022-03-10T10:15:30Z");

try (final MockedStatic<Instant> instantMock = mockStatic(Instant.class)) {
instantMock.when(Instant::now).thenReturn(instantExpected);
final Instant now = Instant.now();
assertEquals(instantExpected, now);
}
}

Unable to mock static methods with parameters using Mockito

This is the correct syntax

fooMock.when( () -> Foo.genMap(any()) ).thenCallRealMethod();

anyString() can be used, but there shouldn't be any difference because it's not ambiguous.

How to mock just one static method in a class using Mockito?

By default all methods are mocked. However, using Mockito.CALLS_REAL_METHODS you can configure the mock to actually trigger the real methods excluding only one.

For example given the class Sample:

class Sample{
static String method1(String s) {
return s;
}
static String method2(String s) {
return s;
}
}

If we want to mock only method1:

@Test
public void singleStaticMethodTest(){
try (MockedStatic<Sample> mocked = Mockito.mockStatic(Sample.class,Mockito.CALLS_REAL_METHODS)) {
mocked.when(() -> Sample.method1(anyString())).thenReturn("bar");
assertEquals("bar", Sample.method1("foo")); // mocked
assertEquals("foo", Sample.method2("foo")); // not mocked
}
}

Be aware that the real Sample.method1() will still be called. From Mockito.CALLS_REAL_METHODS docs:

This implementation can be helpful when working with legacy code. When
this implementation is used, unstubbed methods will delegate to the
real implementation. This is a way to create a partial mock object
that calls real methods by default.
...

Note 1: Stubbing partial mocks using
when(mock.getSomething()).thenReturn(fakeValue) syntax will call the
real method. For partial mock it's recommended to use doReturn
syntax.

So if you don't want to trigger the stubbed static method at all, the solution would be to use the syntax doReturn (as the doc suggests) but for static methods is still not supported:

@Test
public void singleStaticMethodTest() {
try (MockedStatic<Sample> mocked = Mockito.mockStatic(Sample.class,Mockito.CALLS_REAL_METHODS)) {
doReturn("bar").when(mocked).method1(anyString()); // Compilation error!
//...
}
}

About this there is an open issue, in particular check this comment.

Junit5 mock a static method

The short answer is no, as the Mockito team is done with their work and is waiting for the JUnit team for an extension and are discussing here a lot.

With some overhead you can: As JUnit 5 provides support for running legacy JUnit 4, and there you can use Mockito. So you can create tests in Junit4 for these cases:

A sample project for migration setup with gradle and with mvn. From there I am using PowerMock 2.0 beta with Mockito 2.

Mocking static method doesn't work in newer mockito-core version

Could anyone provide any ideas why changing mockito-core is causing this issue?

Sure: PowerMock(ito) doesn't just work with any version of Mockito.

See their documentation:

Mockito PowerMock
2.8.9+ 2.x

See that. 2.8.something. Not 2.28.something

You are simply lucky that 2.23 works, as that version is also not supported.

Another good reason to be really cautious about buying into the PowerMock dependency: it significantly reduces your ability to use newer versions of Mockito.

Thus my personal two cent: the only reasonable use case for PowerMock(ito) is when you have legacy code that can't be tested otherwise. For any other project, especially when writing your own, new code: simply use standalone Mockito. And instead of using the PowerMock hammer to test hard-to-test code, spend some time to learn how to write easy-to-test code.

Long story short: when using PowerMock, you better stick to versions of Mockito that have the "official" approval by Team PowerMock.

If you don't like to be restricted in such ways, I think I told you how to get out of it.



Related Topics



Leave a reply



Submit