Friday, February 10, 2023

Spring Boot Mockito Example

In this article, we will make a shallow dive into mocking using Mockito. We'll be utilising the code from Spring Boot MockMvc Example. We'll be adding a unit test that makes use of Mockito.

We will not dive into much detail on how to build the project as our focus will be on mocking with Mockito. Why do we need to mock? Mocks are useful if you have a dependency to an external system. Such as a database connection, a third party web API, etc. In order for our unit tests to be quick and simple, we need to mock (fake) the database connection, a file read or whatever that may be.

Please refer to the source blog if you want to create the project yourself. Here is the finished project, clone it here, github.com/jpllosa/tdd-mockmvc. We'll just go straight into creating the additional test with mocks.

Mockito

For this example, we are going to pretend that the service layer is a call to a third party web service. Imagine if the third party web service is down, how can you test your code? To be able to run the unit test, we'll need to mock it. Read through the code below. Pay particular attention to getGcfTestWithMockedService(). It will all be explained below.


package com.blogspot.jpllosa.controller;

import static org.hamcrest.CoreMatchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;

import com.blogspot.jpllosa.model.GreatestCommonFactor;
import com.blogspot.jpllosa.service.MathFunService;

@SpringBootTest
@AutoConfigureMockMvc
public class MathFunControllerTest {
	
	@Autowired
	private MockMvc mockMvc;
	
	@InjectMocks
	private MathFunController controller;
	
	@Mock
	private MathFunService service;
	
	@Test
	public void getGcfTest() throws Exception {
		this.mockMvc.perform(get("/get-gcf?firstNumber=12&secondNumber=16"))
			.andDo(print())
			.andExpect(status().isOk())
			.andExpect(jsonPath("$.firstNumber", is(12)))
			.andExpect(jsonPath("$.secondNumber", is(16)))
			.andExpect(jsonPath("$.gcf", is(4)));
	}

	@Test
	public void getGcfTestWithMockedService() throws Exception {
		GreatestCommonFactor gcf = new GreatestCommonFactor();
		gcf.setFirstNumber(12);
		gcf.setSecondNumber(16);
		gcf.setGcf(4);
		
		when(service.findGcf(12, 16)).thenReturn(gcf);
		GreatestCommonFactor actual = controller.getGcf(12, 16);
		
		verify(service).findGcf(anyInt(), anyInt());
		
		assertEquals(4, actual.getGcf());
	}
}

First off, we mark our controllor @InjectMocks. This means mock injection is performed here. Then we mark our service @Mock. This is our mocked object which will be injected to the controller. Now that it's all set up, we go to getGcfTestWithMockedService(). The three things we usually do when mocking an object is: do something when it's called (when), return something based on the call (thenReturn), and verify if the method was actually invoked by our code (verify). Before the controller gets hit, we prepare the service on what to do when findGcf is invoked with 12 and 16 as parameters. When it is invoked, we return a greatest common factor object that we also prepared beforehand. After the controller gets hit, we verify that findGcf was actually invoked. Here, we are happy that any integer was passed. We only care that it was invoked, the third party system does the actual finding of the greatest common factor. We should have two passing tests like below:

Spring Boot Mockito Example
Spring Boot Mockito Example

There you have it. A simple example of how to use Mockito.