You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Lets assume we have class like this and we want to cover it with unit tests:
@RequiredArgsConstructor
public class Concatenator {
private final RandomLong randomLong;
private final RandomString randomString;
public String concat() {
return String.valueOf(randomLong.nextLong()) + " " +randomString.nextString();
}
}
Where its dependencies look like this:
public class RandomString {
public String nextString() {
return new Object().toString();
}
}
public class RandomLong {
public long nextLong() {
return System.currentTimeMillis();
}
}
In most common case unit test for this class will look like this:
class ConcatenatorTest {
// sut
private Concatenator concatenator;
private RandomLong randomLong;
private RandomString randomString;
@AfterEach
void afterEach() {
Mockito.verifyNoMoreInteractions(randomLong);
Mockito.verifyNoMoreInteractions(randomString);
}
@BeforeEach
void beforeEach() {
randomLong = Mockito.mock(RandomLong.class);
randomString = Mockito.mock(RandomString.class);
concatenator = new Concatenator(randomLong, randomString);
}
@Test
void concat() {
// given
Mockito.doReturn(13L).when(randomLong).nextLong();
Mockito.doReturn("15").when(randomString).nextString();
// when
String actual = concatenator.concat();
// then
Mockito.verify(randomLong).nextLong();
Mockito.verify(randomString).nextString();
Assertions.assertEquals("13 15", actual);
}
}
Using MockitoExtension allow you to decrease amount of boiler plate code to some degree:
@ExtendWith(MockitoExtension.class)
class ConcatenatorTest {
// sut
@InjectMocks
private Concatenator concatenator;
@Mock
private RandomLong randomLong;
@Mock
private RandomString randomString;
@AfterEach
void afterEach() {
Mockito.verifyNoMoreInteractions(randomLong);
Mockito.verifyNoMoreInteractions(randomString);
}
@Test
void concat() {
// given
Mockito.doReturn(13L).when(randomLong).nextLong();
Mockito.doReturn("15").when(randomString).nextString();
// when
String actual = concatenator.concat();
// then
Mockito.verify(randomLong).nextLong();
Mockito.verify(randomString).nextString();
Assertions.assertEquals("13 15", actual);
}
}
This looks much better but you still need to cover all mocks and/or spies with Mockito.verifyNoMoreInteractions. My suggestion is to extend existing or implement additional Jupiter extension for Mockito which will handle mentioned verification. So same test will look like this:
@ExtendWith(MockitoExtension.class)
@ExtendWith(MockitoVerifyNoMoreInteractionsExtension.class)
class ConcatenatorTest {
// sut
@InjectMocks
private Concatenator concatenator;
@Mock
private RandomLong randomLong;
@Mock
private RandomString randomString;
@Test
void concat() {
// given
Mockito.doReturn(13L).when(randomLong).nextLong();
Mockito.doReturn("15").when(randomString).nextString();
// when
String actual = concatenator.concat();
// then
Mockito.verify(randomLong).nextLong();
Mockito.verify(randomString).nextString();
Assertions.assertEquals("13 15", actual);
}
}
Where extension implementation may look like this (actually it should use mock api to extract mocks from ExtensionContext but i'm not aware if there is such functionality for now, so i wrote some reflection code to extract mocks from the test class itself) :
public class MockitoVerifyNoMoreInteractionsExtension implements AfterTestExecutionCallback {
@Override
public void afterTestExecution(ExtensionContext context) throws Exception {
Object testInstance = context.getRequiredTestInstance();
Class<?> testClass = context.getRequiredTestClass();
Field[] declaredFields = testClass.getDeclaredFields();
for (Field field : declaredFields) {
field.setAccessible(true);
Object fieldValue = field.get(testInstance);
MockingDetails mockingDetails = Mockito.mockingDetails(fieldValue);
if (mockingDetails.isMock() || mockingDetails.isSpy()) {
Mockito.verifyNoMoreInteractions(fieldValue);
}
}
}
}
What this code does: it picks up test instance class from the ExtensionContext, extract all declared fields which identified as mocks or spies and run verify no more interactions for those fields.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Lets assume we have class like this and we want to cover it with unit tests:
Where its dependencies look like this:
In most common case unit test for this class will look like this:
Using
MockitoExtension
allow you to decrease amount of boiler plate code to some degree:This looks much better but you still need to cover all mocks and/or spies with
Mockito.verifyNoMoreInteractions
. My suggestion is to extend existing or implement additional Jupiter extension for Mockito which will handle mentioned verification. So same test will look like this:Where extension implementation may look like this (actually it should use mock api to extract mocks from ExtensionContext but i'm not aware if there is such functionality for now, so i wrote some reflection code to extract mocks from the test class itself) :
What this code does: it picks up test instance class from the ExtensionContext, extract all declared fields which identified as mocks or spies and run
verify no more interactions
for those fields.Beta Was this translation helpful? Give feedback.
All reactions