SpringBoot – Unit Testing with Mockito
https://frontbackend.com/spring-boot/spring-boot-2-junit-5-mockito
-
Application - the main Spring Boot application class used for starting web container,
-
TestReportController - Spring Rest Controller for testing purposes,
-
TestReportRepository - Spring Service used to check how autowire works in tests,
-
TestReportControllerMockitoTest - test for TestReportControllerusing Mockito,
-
TestReportControllerMockMvcTest - test for TestReportControllerusing MockMvc,
-
TestReportCon*RestTemplTest - test for TestReportController using TestRestTemplate.
Controller Class
@RestController
public class TestReportController {
@Autowired
TestReportRepository repository;
@Autowired
RestTemplate restTemplate;
@GetMapping("/testReport/{id}")
public ResponseEntity<TestReport> getReportByID(@PathVariable int id){
TestReport data = repository.getById(id);
return ResponseEntity
.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(data);
}
@GetMapping(value = "/testReport/all", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<TestReport>> getAllTestReports() {
List<TestReport> reports = new ArrayList<TestReport>();
repository.findAll().forEach(reports::add);
if (reports.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(reports, HttpStatus.OK);
}
@GetMapping(value = "/testReport/mi", produces = "application/json")
public ResponseEntity<Object> getAllMIReportsNoramlcall() {
ResponseEntity<Object> responseEntity = restTemplate.getForEntity("http://MI-MICROSERVICE/mi/anthology/all", Object.class);
return new ResponseEntity<>(responseEntity, HttpStatus.OK);
}
}
Using MockMvc -
We used the MockMvc class and @AutoConfigureMockMvc that will configure it and inject it into the tested class. The MockMvc class is used to perform API calls, but instead of doing HTTP requests, Spring will test only the implementation that handle them in TestReportController
@SpringBootTest
@AutoConfigureMockMvc
public class TestReportControllerMockMvcTest {
@Autowired
private MockMvc mockMvc;
@Test
public void getReportByIDTest() throws Exception{
this.mockMvc.perform(get("/testReport/1"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string(containsString("Bllod Report")));
}
@Test
public void getAllResportsTest() throws Exception{
this.mockMvc.perform(get("/testReport/all"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
}
}
Using TestRestTemplate
Spring boot test that makes use of TestRestTemplate to call REST API. In this approach, we can use @Autowired annotation just like in runtime applications. Spring will interpret them and do the necessary injections. In @SpringBootTest tests real Spring Boot application server is being started.
In the test we used:
-
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT - to start the server with a random port in order to avoid any port conflicts,
-
@LocalServerPort - this annotation tells Spring to inject a random port to the specific field,
-
TestRestTemplate - RestTemplate for tests used to make a real HTTP requests.
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestReportControllerRestTemplateTest
@LocalServerPort
private int port;
private String url;
@Autowired
private TestRestTemplate restTemplate;
@BeforeEach
public void setUp() {
url = String.format("http://localhost:%d/", port);
}
@Test
public void greetingShouldReturnDefaultMessage() {
assertThat(this.restTemplate.getForObject(url+"testreport/1", String.class)).contains("Report");
}
}
Using Mockito unit test
-
@Mock creates a mock.
-
@InjectMocks creates an instance of the class and injects the mocks that are created with the @Mock (or @Spy) annotations into this instance.
In above controller class has two dependent objects. First, we need to mock those objects & then we need to inject them to main controller class
@Autowired
TestReportRepository repository;
@Autowired
RestTemplate restTemplate;
@ExtendWith(MockitoExtension.class)
public class TestReportControllerTest {
@Mock
TestReportRepository repository;
@Mock
RestTemplate restTemplate;
@InjectMocks
TestReportController controller;
@BeforeEach
void setMockOutput() {
when(repository.getById(1)).thenReturn(new TestReport("Report", "", "", "", ""));
}
@Test
public void getReportByIDTest() throws Exception{
assertThat(controller.getReportByID(1).getBody().getReportName().equalsIgnoreCase("Report"));
}
}
If any datasource object is their, just mock annotation will automatically creates Connection object with test properties. Try adding your datasource as a @MockBean too:
@MockBean
private DataSource dataSource