Using Mockito annotations in JUnit 5. The latest release of JUnit is not backward compatible and does not work straightforwardly with many testing frameworks such as Mockito. Mockito annotations do not work in JUnit 5 by default. It needs some tweaks to make it work. In this article, we cover how to make Mockito annotations work with JUnit 5.
Assuming that we have a test class written in JUnit 4 and we want to convert it to JUnit 5.
import com.madadipouya.junit5.service.StudentService;
import com.madadipouya.junit5.service.CourseService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.Mockito.*;
import static org.junit.Assert.assertEquals;
@RunWith(MockitoJUnitRunner.class)
public class StudentServiceJUnit4Test {
@InjectMocks
private StudentService studentService;
@Mock
private CourseService courseService;
@Test
public void testCalculatingCGPA() {
long studentId = 1L
when(courseService.getAllCoursesByStudentId(anyLong())).thenReturn(stubCourses());
long cGPA = studentService.getCGPA(studentId);
verify(courseService, times(1)).getAllCoursesByStudentId(anyLong());
assertEquals("3.4", cGPA);
}
}
The test can be converted to JUnit 5 in two ways.
Injecting the dependencies manually to the class under the test
import com.madadipouya.junit5.service.StudentService;
import com.madadipouya.juni5.service.CourseService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class StudentServiceJUnit5NoAnnotationTest {
private StudentService studentService;
private CourseService courseService;
@BeforeEach
public void setup() {
courseService = mock(CourseService.class);
studentService = new StudentService(courseService);
}
@Test
public void testCalculatingCGPA() {
long studentId = 1L
when(courseService.getAllCoursesByStudentId(anyLong())).thenReturn(stubCourses());
long cGPA = studentService.getCGPA(studentId);
verify(courseService, times(1)).getAllCoursesByStudentId(anyLong());
assertEquals("3.4", cGPA);
}
}
As you can, in this approach no Mockito annotation is used and all dependencies are injected into the class manually using the constructor injection.
The problems with this approach are:
- The class under the test should support constructor dependency injection. It does not work with the field dependency injection.
- If a new dependency added, the code will not compile.
- Every time a test case runs, a new instance of the class is created.
- Much of the test class need to be changed.
Hence a better way is to keep using Mockito annotations.
Adding mockito-junit-jupiter
package and using annotations
Mockito has a JUnit 5 compatibility package that allows the tests written in JUnit 5 to work with Mockito annotations easily. All that needed is to add an extra dependency to the project like this:
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.22.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>2.22.0</version>
<scope>test</scope>
</dependency>
</dependencies>
As you can see beside mockito-code
, there is a mockito-junit-jupiter
dependency which helps us to leverage on Mockito annotations in JUnit 5.
After adding the new Mockito dependency the next step is to rewrite the code in JUnit 5. The final code will look like this:
import com.madadipouya.junit5.service.StudentService;
import com.madadipouya.juni5.service.CourseService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ExtendWith(MockitoExtension.class)
public class StudentServiceJUnit5WithAnnotationTest {
@InjectMocks
private StudentService studentService;
@Mock
private CourseService courseService;
@Test
public void testCalculatingCGPA() {
long studentId = 1L
when(courseService.getAllCoursesByStudentId(anyLong())).thenReturn(stubCourses());
long cGPA = studentService.getCGPA(studentId);
verify(courseService, times(1)).getAllCoursesByStudentId(anyLong());
assertEquals("3.4", cGPA);
}
}
As you can see, we replaced @RunWith
with @ExtendWith
as the former does not exist in JUnit 5 anymore. Then we passed MockitoExtension.class
to the annotation which is coming from mockito-junit-jupiter
package. And lastly, replaced @Test
with the JUnit 5 counterpart.
Happy testing!