Add support for final mocks
Hey folks,
in Collibra some teams have been writing tests bypassing Mockito annotations so that test fields can be declared as final.
This unfortunately comes with a downside: it seems MockitoExtension does not take those mocks into account those mocks. In other words, those tests can do over stubbing and the extension does not protect them.
Please find hereby a test class I've just written to prove my point:
package com.collibra;
import com.collibra.test.mockito.MockitoLightweightExtension;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.UUID;
import static java.util.UUID.randomUUID;
import static org.assertj.core.api.BDDAssertions.then;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
class LightweightTest {
interface Service {
Object getById(UUID id);
}
@RequiredArgsConstructor
static class Api {
private final Service service;
@SuppressWarnings("UnusedReturnValue")
Object getById(UUID id) {
return service.getById(id);
}
}
abstract static class SimpleTest {
private final Object expected = new Object();
private final UUID id = randomUUID();
abstract Service getService();
abstract Api getApi();
@Test
void test() {
// given
var service = getService();
given(service.getById(id)).willReturn(expected);
// over-stubbing
given(service.getById(randomUUID())).willReturn(new Object());
// when
var actual = getApi().getById(id);
// then
then(actual).isSameAs(expected);
}
}
@Nested
@ExtendWith(MockitoExtension.class)
@Getter
class WithAnnotationsOverStubbingIsDetected extends SimpleTest {
@Mock
private Service service;
@InjectMocks
private Api api;
}
@Nested
@ExtendWith(MockitoExtension.class)
@Getter
class WithFinalFieldsTestPasses extends SimpleTest {
private final Service service = mock();
private final Api api = new Api(service);
}
/**
* This also does not detect it and supposedly I'm following what's described in {@link MockitoExtension}'s
* javadoc. Am I missing something? Or am I hitting a bug?
*/
@Nested
@ExtendWith(MockitoExtension.class)
@Getter
class WithConstructorInjectionTestPasses extends SimpleTest {
private final Api api;
private final Service service;
WithConstructorInjectionTestPasses(@Mock Service service) {
this.service = service;
api = new Api(service);
}
}
@Nested
@ExtendWith(MockitoLightweightExtension.class)
@Getter
class WithFinalFieldsAndLightWeightedTestFails extends SimpleTest {
private final Service service = mock();
private final Api api = new Api(service);
}
}This issue relates with https://github.com/mockito/mockito/issues/3231 (which I've just closed as staled) but now the POV is different: it's not about performance, it is about detecting over stubbing.
The PR https://github.com/mockito/mockito/pull/3232 related to the closed issue This issue relates with https://github.com/mockito/mockito/issues/3231 still contain the definition of this custom extension. I've just rebased it and aligned it with the last version of our in house version
Source: mockito/mockito