Mockito: An Unit Testing Framework

Mockito is a Java based mocking framework most preferred with the Junit testing framework. It internally uses Java Reflection and allows to create dummy objects of service.

Mockito is a very useful tool that simplifies the test case creation by creating mock/dummy object of external dependencies this dummy data will later apply with mocks into code under test.

Maven Dependencies for Mockito :

To Use Mockito in your Project tests add following dependencies into pom.xml file.

<dependency>
     <groupId>org.mockito</groupId>
     <artifactId>mockito-core</artifactId>
    <version>3.3.3</version>
     <scope>test</scope>
</dependency>
<dependency>
     <groupId>org.mockito</groupId>
     <artifactId>mockito-junit-jupiter</artifactId>
    <version>3.3.3</version>
     <scope>test</scope>
</dependency>

mockito-juint-jupiter dependency is only used for Juint5 tests if you are using Juint4 then you only need mockito-core dependency.

Some of Common Guidelines to write test case:

  1. Prefer literals instead of generated objects/values.
  2. Avoid for loop as much as you can use java streams if needed.
  3. Avoid assertions of Date parameters as it is more possibility to generate different timestamp at actual code call and test case time (Test can’t control time).
  4. While writing Unit Test always uses mock to dependencies present in class-under-test.
  5. Keep test as small as possible 3-4 lines maximum, test does not contain any logic code.
  6. Test methods must be independent do not write test methods dependent on the result generated by other test methods.

Different Annotations and Their Purpose:

  1. @WebMvcTest(ClassNameUnderTest.class) :

This Annotation is used for Spring MVC Test that focuses on Spring MVC Components. Using this annotation will disable full-auto-configuration and instead only apply only configuration relevant to MVC test components such as @Controller, @Service, @JsonComponent or @Repository so on.

2. @RunWith(AnyRunner.class) :

When a class is annotated with @RunWith or extends a class annotated with @RunWith, JUnit will invoke the class it references to run the tests in that class instead of the runner built into Junit.

For example @RunWith(SpringRunner.class) will run the tests with SpringRunner instead of built in Juint Runner.

3. @DisplayName(“Suitable Name for Test”) :

Display Name is used to declare any costume name for tests to recognize it after run. Display names are typically used for test reporting in IDEs and build tools and may contain spaces, special characters, and even emoji.

4. MockMvc

Main entry point for server-side Spring MVC test support.

5. @Mock:

Mark a field as a mock. Allows shorthand mock creation. Minimizes repetitive mock creation code. It makes the test class more readable & the verification error easier to read because the field name is used to identify the mock.

6. @MockBean:

Annotation that can be used to add mocks to a Spring @ApplicationContext. It can be used as a class level annotation or on fields in either @Configuration classes, or test classes that are @RunWith the @SpringRunner or linked with respective.

Mocks can be registered by type or by bean name. Any existing single bean of the same type defined in the context will be replaced by the mock. If no existing bean is defined a new one will be added. Dependencies that are known to the application context but are not beans will not be found and a mocked bean will be added to the context alongside the existing dependency.

7. @InjectMocks:

Mark a field on which injection should be performed. Allows shorthand mock and spy injection. Minimizes repetitive mock and spy injection. Mockito will try to inject mocks only either by constructor injection, setter injection, or property injection in order and as described below.

All the mocks created with @Mock and @MockBean will be get automatically injected into an object created using @InjectMocks.

8. @BeforeEach :

This is used to signal that the annotated method should be executed before each tests or test methods in the current test class. Method which is annotated with @BeforeEachethods must have a void return type, must not be private, and must not be static. They may optionally declare parameters.

9. @Test :

@Test is used to signal that the annotated method is a test method. @Test methods must not be private or static and must not return a value.

Empty Test Always pass. All test without assertions in it is always going to pass.

Above are some Annotations used for testing using Mockito.

To Mock Method Calls by using mockito :

1. Mock method with return type

To Mock method calls of methods with return type Mockito.when() and thenReturn() methods are used on the mock objects

For example

public Integer addNumbers(int a, int b) {

return a + b;

}

is a method you can mock this method call by which you can control output/result of the method is testing framework as follows,

Mockito.when(mock_object_of_class.addNumbers(anyInt(), anyInt())).thenReturn(100);

anyInt(), any(), anyString() are some of ArgumentMatchers method used at time of testing to specify type/class of parameters.

2. Mock void method :

To mock void methods best way to verify method has called while testing. To very method verify() is used

For Example :

public void printAddition(Integer a, Integer b){

System.out.println(“Addition Is : “+ (a+b));

}

In Mocito it is preferred way to verify the method call for void methods instead of mocking method call. To verify this method we can use verify() with mock_object_of_object

Mockito.verify(mock_object_of_class, times(1)).printAddition(1 , 2);

Example for Mockito :

Controller :

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping(value = "/user")
    public Acknowledge addNewUser(@RequestBody User user){
        return userService.addNewUser(user);
    }
}

Repository :

public interface UserRepository extends CrudRepository<User, Integer> {

}

Service :

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public Acknowledge addNewUser(User user) {
        userRepository.save(user);

        return new Acknowledge(user.getId(), user.getUserName());
    }
}

Test For UserController :

@WebMvcTest(UserController.class)
@RunWith(SpringRunner.class)
@DisplayName("User Controller Test For")
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Mock
    private String userMock;

    @BeforeEach
    void setup(){
        userMock = String.format("{ \"userName\" : \"%s\", \"email\" : \"%s\" }","testUser","user@test.com");
    }
    @Nested
    @DisplayName("Add User With")
    class TestAddUser {
        @Test
        @DisplayName("Valid User Object")
        void addNewUser() throws Exception {
            RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/user")
                                                .content(userMock)
                                                .contentType(MediaType.APPLICATION_JSON)
                                                .accept(MediaType.APPLICATION_JSON);

            mockMvc.perform(requestBuilder)
                    .andExpect(status().isOk())
                    .andReturn();
        }
    }
}

Test For Service :

@WebMvcTest(UserService.class)
@RunWith(SpringJUnit4ClassRunner.class)
@DisplayName("UserService test with")
class UserServiceTest {

    @Mock
    private UserRepository userRepositoryMockBean;

    @MockBean
    private CommanService commanServiceMockBean;

    @InjectMocks
    private UserService userServiceInjectMocks;

    private User userMock;

    @BeforeEach
    void setup(){
        userMock = new User("testUser","user@test.com");
    }

    @Test
    @DisplayName("Valid User Object")
    void addNewUser() {
        ResponseEntity<?> responseEntity = userServiceInjectMocks.addNewUser(userMock);

        assertEquals(responseEntity.getStatusCode(), HttpStatus.CREATED);

        Acknowledge acknowledge = (Acknowledge) responseEntity.getBody();
        assertEquals(userMock.getUserName(), acknowledge.getUserName());
    }
}

Most Common Errors/Exception in testing project :

1. Failed To Load Into ApplicationContext :

IllegalStateException: Failed to load ApplicationContext :

This occurs when SpringBoot failed to add/load bean classes into ApplicationContext because the bean you are using as internal/external dependencies is not loaded into test ApplicationContext.

To Resolve this :

  1. Try to Use @MockBean annotation for your external/extended dependencies.
  2. Try to move your ApplicationContext into package that contains tests.

2. No tests are found :

This error message is due to missing JUnit dependencies to resolve this problem please check you are using updated dependencies Versions of JUnit.

Conclusion :

In this article, we saw how to create mock objects/dependency and how to use the mock objects.

The source code that accompanies this article is available on GitHub.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.