Java Spring Junit5 Controller Testing Examples

Nowadays Unit Test is so important in Software Development, and Spring Boot Test also provides @WebMvcTest annotation to make writing unit test for Rest Controller more simpler. In this tutorial, we're gonna look at how to apply @WebMvcTest in our Spring Boot Project with JUnit 5 and Mockito.

More Practice:
– @DataJpaTest example in Spring Boot
– @RestControllerAdvice example in Spring Boot
– Spring Boot Token based Authentication with Spring Security example

Associations:
– Spring Boot One To Many example with JPA, Hibernate
– Spring Boot Many to Many example with JPA, Hibernate

Contents

  • Spring Boot test Rest Controller Overview
  • Spring Boot @WebMvcTest
  • Spring Boot test Rest Controller example
    • Technology
    • Project Structure
    • Setup Spring Boot Test Rest Controller Project
    • Spring Boot @WebMvcTest example
  • Run Spring Boot Rest API unit testing with Junit 5
  • Conclusion
  • Further Reading
  • Source Code

Spring Boot test Rest Controller Overview

We've created Rest Controller for CRUD Operations and finder method.
Let look at the code:
(step by step to build the Rest APIs is in:
– Spring Boot + H2
– Spring Boot + MySQL
– Spring Boot + PostgreSQL
– Spring Data + MongoDB
– Spring JPA + SQL Server
– Spring JPA + Oracle
– Spring Data + Cassandra)

          @RestController public class TutorialController {   @Autowired   TutorialRepository tutorialRepository;   @GetMapping("/tutorials")   public ResponseEntity<List<Tutorial>> getAllTutorials(@RequestParam(required = false) String title) {     try {       ...       return new ResponseEntity<>(tutorials, HttpStatus.OK);     } catch (Exception e) {       return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);     }   }      @GetMapping("/tutorials/{id}")   public ResponseEntity<Tutorial> getTutorialById(@PathVariable("id") long id) {     Optional<Tutorial> tutorialData = tutorialRepository.findById(id);     if (tutorialData.isPresent()) {       return new ResponseEntity<>(tutorialData.get(), HttpStatus.OK);     } else {       return new ResponseEntity<>(HttpStatus.NOT_FOUND);     }   }      @PutMapping("/tutorials/{id}")   public ResponseEntity<Tutorial> updateTutorial(@PathVariable("id") long id, @RequestBody Tutorial tutorial) {     Optional<Tutorial> tutorialData = tutorialRepository.findById(id);     if (tutorialData.isPresent()) {       ...       return new ResponseEntity<>(tutorialRepository.save(_tutorial), HttpStatus.OK);     } else {       return new ResponseEntity<>(HttpStatus.NOT_FOUND);     }   }   ...   @DeleteMapping("/tutorials/{id}")   public ResponseEntity<HttpStatus> deleteTutorial(@PathVariable("id") long id) {     try {       tutorialRepository.deleteById(id);       return new ResponseEntity<>(HttpStatus.NO_CONTENT);     } catch (Exception e) {       return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);     }   }   @DeleteMapping("/tutorials")   public ResponseEntity<HttpStatus> deleteAllTutorials() {     // try and catch   } }                  

Is there any way to test Rest Controller in Spring Boot? Any way to:
– know that an HTTP request is mapped to correct endpoints (with @GetMapping, @PostMapping, @PutMapping and @DeleteMapping)
– know that path variables are mapped with @PathVariable
– know that the @RequestBody and @ResponseBody work correctly

Let's write the test cases with @WebMvcTest and Mockito, then run Spring Boot test Rest Controller with JUnit 5.

spring boot rest api unit testing with junit 5

For testing, we'll work with H2 in-memory database. It eliminates the need for configuring and starting an actual database.

Spring Boot @WebMvcTest

Spring Boot @WebMvcTest annotation provides simple way to test Rest Controller, it disables full auto-configuration (@Component, @Service or @Repository beans will not be scanned) and apply only configuration relevant to the web layer (@Controller, @ControllerAdvice, @JsonComponent, WebMvcConfigurer beans…).

If you have multiple controllers, you can make only one instantiate by using @WebMvcTest(TutorialController.class).

          @WebMvcTest(TutorialController.class) public class TutorialControllerTests { }                  

Now look at our Rest Controller (TutorialController) which has a dependency component TutorialRepository:

          @RestController public class TutorialController {   @Autowired   TutorialRepository tutorialRepository;   ... }                  

Spring automatically injects the dependency into the controller.

Let's think about the test class.

– We need to use @MockBean annotation to create and inject a mock of the TutorialRepository. It helps application context start), then we will set the expectations using Mockito.

– We need to fake HTTP requests. So we will autowire a MockMvc bean which Spring Boot autoconfigures.

          @WebMvcTest(TutorialController.class) public class TutorialControllerTests {   @MockBean   private TutorialRepository tutorialRepository;   @Autowired   private MockMvc mockMvc;   @Test   void shouldDoSomething() throws Exception {     // set expectation for TutorialRepository using Mockito     when(tutorialRepository...).thenReturn(...);     // perform HTTP request and set the expectations with MockMVC     mockMvc.perform(...).andExpect(...).andExpect(...);   } }                  

Spring Boot test Rest Controller example

Technology

  • Spring Boot 2
  • Mockito 4
  • Junit 5
  • Maven 3.8.5
  • H2 database

Project Structure

spring-boot-test-rest-controller-project-structure

Tutorial data model class corresponds to entity and table tutorials.
TutorialRepository handles CRUD methods and custom finder methods. It will be autowired in TutorialController and mocked in TutorialControllerTests.
TutorialControllerTests is the main Test Class used for testing Rest Controller and annotated with @WebMvcTest.
– pom.xml contains dependencies for Spring Boot Test, Web, Spring Data, H2 database.

Setup Spring Boot Test Rest Controller Project

This tutorial gives you an additional unit test for following Rest APIs example:
– Spring Boot + H2
– Spring Boot + MySQL
– Spring Boot + PostgreSQL
– Spring Data + MongoDB
– Spring JPA + SQL Server
– Spring JPA + Oracle
– Spring Data + Cassandra

So you only need to create a new file named TutorialControllerTests.java in src/test/java/[package].

This is a typical pom.xml with dependencies:

          <dependency> 	<groupId>org.springframework.boot</groupId> 	<artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> 	<groupId>org.springframework.boot</groupId> 	<artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> 	<groupId>org.springframework.boot</groupId> 	<artifactId>spring-boot-starter-test</artifactId> 	<scope>test</scope> </dependency> <dependency> 	<groupId>com.h2database</groupId> 	<artifactId>h2</artifactId> 	<scope>test</scope> </dependency>                  

Spring Boot @WebMvcTest example

Let's apply @WebMvcTest in our Spring Boot Rest API Unit Test with JUnit 5 and Mockito.

In src/test/java/com.bezkoder.spring.test, create a file with name: TutorialControllerTests.java.

Inside the file, we will define TutorialControllerTests to unit test the Rest API endpoints which has following methods:

  • shouldCreateTutorial(): POST /api/tutorials
  • shouldReturnTutorial(): GET /api/tutorials/[:id]
  • shouldReturnNotFoundTutorial(): GET /api/tutorials/[:id] – 404
  • shouldReturnListOfTutorials(): GET /api/tutorials
  • shouldReturnListOfTutorialsWithFilter(): GET /api/tutorials?title=[:title]
  • shouldReturnNoContentWhenFilter(): GET /api/tutorials?title=[:title] – 204
  • shouldUpdateTutorial(): PUT /api/tutorials/[:id]
  • shouldReturnNotFoundUpdateTutorial(): PUT /api/tutorials/[:id] – 404
  • shouldDeleteTutorial(): DELETE /api/tutorials/[:id]
  • shouldDeleteAllTutorials(): DELETE /api/tutorials
          package com.bezkoder.spring.test; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import com.bezkoder.spring.test.controller.TutorialController; import com.bezkoder.spring.test.model.Tutorial; import com.bezkoder.spring.test.repository.TutorialRepository; import com.fasterxml.jackson.databind.ObjectMapper; @WebMvcTest(TutorialController.class) public class TutorialControllerTests {   @MockBean   private TutorialRepository tutorialRepository;   @Autowired   private MockMvc mockMvc;   @Autowired   private ObjectMapper objectMapper;   @Test   void shouldCreateTutorial() throws Exception {     Tutorial tutorial = new Tutorial(1, "Spring Boot @WebMvcTest", "Description", true);     mockMvc.perform(post("/api/tutorials").contentType(MediaType.APPLICATION_JSON)         .content(objectMapper.writeValueAsString(tutorial)))         .andExpect(status().isCreated())         .andDo(print());   }   @Test   void shouldReturnTutorial() throws Exception {     long id = 1L;     Tutorial tutorial = new Tutorial(id, "Spring Boot @WebMvcTest", "Description", true);     when(tutorialRepository.findById(id)).thenReturn(Optional.of(tutorial));     mockMvc.perform(get("/api/tutorials/{id}", id)).andExpect(status().isOk())         .andExpect(jsonPath("$.id").value(id))         .andExpect(jsonPath("$.title").value(tutorial.getTitle()))         .andExpect(jsonPath("$.description").value(tutorial.getDescription()))         .andExpect(jsonPath("$.published").value(tutorial.isPublished()))         .andDo(print());   }   @Test   void shouldReturnNotFoundTutorial() throws Exception {     long id = 1L;     when(tutorialRepository.findById(id)).thenReturn(Optional.empty());     mockMvc.perform(get("/api/tutorials/{id}", id))          .andExpect(status().isNotFound())          .andDo(print());   }   @Test   void shouldReturnListOfTutorials() throws Exception {     List<Tutorial> tutorials = new ArrayList<>(         Arrays.asList(new Tutorial(1, "Spring Boot @WebMvcTest 1", "Description 1", true),             new Tutorial(2, "Spring Boot @WebMvcTest 2", "Description 2", true),             new Tutorial(3, "Spring Boot @WebMvcTest 3", "Description 3", true)));     when(tutorialRepository.findAll()).thenReturn(tutorials);     mockMvc.perform(get("/api/tutorials"))         .andExpect(status().isOk())         .andExpect(jsonPath("$.size()").value(tutorials.size()))         .andDo(print());   }   @Test   void shouldReturnListOfTutorialsWithFilter() throws Exception {     List<Tutorial> tutorials = new ArrayList<>(         Arrays.asList(new Tutorial(1, "Spring Boot @WebMvcTest", "Description 1", true),             new Tutorial(3, "Spring Boot Web MVC", "Description 3", true)));     String title = "Boot";     MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();     paramsMap.add("title", title);     when(tutorialRepository.findByTitleContaining(title)).thenReturn(tutorials);     mockMvc.perform(get("/api/tutorials").params(paramsMap))         .andExpect(status().isOk())         .andExpect(jsonPath("$.size()").value(tutorials.size()))         .andDo(print());     tutorials = Collections.emptyList();     when(tutorialRepository.findByTitleContaining(title)).thenReturn(tutorials);     mockMvc.perform(get("/api/tutorials").params(paramsMap))         .andExpect(status().isNoContent())         .andDo(print());   }      @Test   void shouldReturnNoContentWhenFilter() throws Exception {     String title = "BezKoder";     MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();     paramsMap.add("title", title);          List<Tutorial> tutorials = Collections.emptyList();     when(tutorialRepository.findByTitleContaining(title)).thenReturn(tutorials);     mockMvc.perform(get("/api/tutorials").params(paramsMap))         .andExpect(status().isNoContent())         .andDo(print());   }   @Test   void shouldUpdateTutorial() throws Exception {     long id = 1L;     Tutorial tutorial = new Tutorial(id, "Spring Boot @WebMvcTest", "Description", false);     Tutorial updatedtutorial = new Tutorial(id, "Updated", "Updated", true);     when(tutorialRepository.findById(id)).thenReturn(Optional.of(tutorial));     when(tutorialRepository.save(any(Tutorial.class))).thenReturn(updatedtutorial);     mockMvc.perform(put("/api/tutorials/{id}", id).contentType(MediaType.APPLICATION_JSON)         .content(objectMapper.writeValueAsString(updatedtutorial)))         .andExpect(status().isOk())         .andExpect(jsonPath("$.title").value(updatedtutorial.getTitle()))         .andExpect(jsonPath("$.description").value(updatedtutorial.getDescription()))         .andExpect(jsonPath("$.published").value(updatedtutorial.isPublished()))         .andDo(print());   }      @Test   void shouldReturnNotFoundUpdateTutorial() throws Exception {     long id = 1L;     Tutorial updatedtutorial = new Tutorial(id, "Updated", "Updated", true);     when(tutorialRepository.findById(id)).thenReturn(Optional.empty());     when(tutorialRepository.save(any(Tutorial.class))).thenReturn(updatedtutorial);     mockMvc.perform(put("/api/tutorials/{id}", id).contentType(MediaType.APPLICATION_JSON)         .content(objectMapper.writeValueAsString(updatedtutorial)))         .andExpect(status().isNotFound())         .andDo(print());   }      @Test   void shouldDeleteTutorial() throws Exception {     long id = 1L;     doNothing().when(tutorialRepository).deleteById(id);     mockMvc.perform(delete("/api/tutorials/{id}", id))          .andExpect(status().isNoContent())          .andDo(print());   }      @Test   void shouldDeleteAllTutorials() throws Exception {     doNothing().when(tutorialRepository).deleteAll();     mockMvc.perform(delete("/api/tutorials"))          .andExpect(status().isNoContent())          .andDo(print());   } }                  

Run Spring Boot Rest API unit testing with Junit 5

– First run command: mvn clean install.
– Then run Test: mvn test -Dtest=TutorialControllerTests

Here are several examples and the result:

          MockHttpServletRequest:       HTTP Method = GET       Request URI = /api/tutorials/1        Parameters = {}           Headers = []              Body = null     Session Attrs = {} Handler:              Type = com.bezkoder.spring.test.controller.TutorialController            Method = com.bezkoder.spring.test.controller.TutorialController#getTutorialById(long) Async:     Async started = false      Async result = null Resolved Exception:              Type = null ModelAndView:         View name = null              View = null             Model = null FlashMap:        Attributes = null MockHttpServletResponse:            Status = 200     Error message = null           Headers = [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", Content-Type:"application/json"]      Content type = application/json              Body = {"id":1,"title":"Spring Boot @WebMvcTest","description":"Description","published":true}     Forwarded URL = null    Redirected URL = null           Cookies = [] [... ... ...] MockHttpServletRequest:       HTTP Method = GET       Request URI = /api/tutorials        Parameters = {title=[Boot]}           Headers = []              Body = null     Session Attrs = {} Handler:              Type = com.bezkoder.spring.test.controller.TutorialController            Method = com.bezkoder.spring.test.controller.TutorialController#getAllTutorials(String) Async:     Async started = false      Async result = null Resolved Exception:              Type = null ModelAndView:         View name = null              View = null             Model = null FlashMap:        Attributes = null MockHttpServletResponse:            Status = 200     Error message = null           Headers = [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", Content-Type:"application/json"]      Content type = application/json              Body = [{"id":1,"title":"Spring Boot @WebMvcTest","description":"Description 1","published":true},{"id":3,"title":"Spring Boot Web MVC","description":"Description 3","published":true}]     Forwarded URL = null    Redirected URL = null           Cookies = [] [... ... ...] MockHttpServletRequest:       HTTP Method = PUT       Request URI = /api/tutorials/1        Parameters = {}           Headers = [Content-Type:"application/json;charset=UTF-8", Content-Length:"67"]              Body = {"id":1,"title":"Updated","description":"Updated","published":true}     Session Attrs = {} Handler:              Type = com.bezkoder.spring.test.controller.TutorialController            Method = com.bezkoder.spring.test.controller.TutorialController#updateTutorial(long, Tutorial) Async:     Async started = false      Async result = null Resolved Exception:              Type = null ModelAndView:         View name = null              View = null             Model = null FlashMap:        Attributes = null MockHttpServletResponse:            Status = 200     Error message = null           Headers = [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", Content-Type:"application/json"]      Content type = application/json              Body = {"id":1,"title":"Updated","description":"Updated","published":true}     Forwarded URL = null    Redirected URL = null           Cookies = [] MockHttpServletRequest:       HTTP Method = POST       Request URI = /api/tutorials        Parameters = {}           Headers = [Content-Type:"application/json;charset=UTF-8", Content-Length:"87"]              Body = {"id":1,"title":"Spring Boot @WebMvcTest","description":"Description","published":true}     Session Attrs = {} Handler:              Type = com.bezkoder.spring.test.controller.TutorialController            Method = com.bezkoder.spring.test.controller.TutorialController#createTutorial(Tutorial) Async:     Async started = false      Async result = null Resolved Exception:              Type = null ModelAndView:         View name = null              View = null             Model = null FlashMap:        Attributes = null MockHttpServletResponse:            Status = 201     Error message = null           Headers = [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"]      Content type = null              Body =     Forwarded URL = null    Redirected URL = null           Cookies = [] [... ... ...] [INFO] Tests run: 10, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.436 s - in com.bezkoder.spring.test.TutorialControllerTests [INFO] [INFO] Results: [INFO] [INFO] Tests run: 10, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time:  5.151 s [INFO] Finished at: 2022-03-28T10:15:22+07:00 [INFO] ------------------------------------------------------------------------                  

We have another way to run Junit 5 test in Spring Tool Suite with UI.
In Package Explorer, Right Click on TutorialControllerTests.java -> Run as -> JUnit Test:

spring boot rest api unit testing with junit 5

Conclusion

Today we've create Spring Boot Test for Rest Controller with Junit 5 using @WebMvcTest and Mockito. We also run unit test for many CRUD and finder methods endpoints.

You may need to handle Exception with:
Spring Boot @ControllerAdvice & @ExceptionHandler example

Happy learning! See you again.

Further Reading

  • @WebMvcTest annotation
  • Auto-configured Spring MVC Tests

More Practice:
– @DataJpaTest example in Spring Boot
– @RestControllerAdvice example in Spring Boot
– Spring Boot Token based Authentication with Spring Security example

Associations:
– Spring Boot One To Many example with JPA, Hibernate
– Spring Boot Many to Many example with JPA, Hibernate

Source Code

You can find the complete source code for this tutorial on Github.

The code gives you an additional unit test for following Rest APIs example:
– Spring Boot + H2
– Spring Boot + MySQL
– Spring Boot + PostgreSQL
– Spring Data + MongoDB
– Spring JPA + SQL Server
– Spring JPA + Oracle
– Spring Data + Cassandra

binionandeed.blogspot.com

Source: https://www.bezkoder.com/spring-boot-webmvctest/

0 Response to "Java Spring Junit5 Controller Testing Examples"

Postar um comentário

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel