Using Mockito annotations in JUnit 5

Default featured post

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:

  1. The class under the test should support constructor dependency injection. It does not work with the field dependency injection.
  2. If a new dependency added, the code will not compile.
  3. Every time a test case runs, a new instance of the class is created.
  4. 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!