Java unit test controller

Spring Boot — Rest Controller Unit Test

Spring Boot provides an easy way to write a Unit Test for Rest Controller file. With the help of SpringJUnit4ClassRunner and MockMvc, we can create a web application context to write Unit Test for Rest Controller file.

Unit Tests should be written under the src/test/java directory and classpath resources for writing a test should be placed under the src/test/resources directory.

For Writing a Unit Test, we need to add the Spring Boot Starter Test dependency in your build configuration file as shown below.

 org.springframework.boot spring-boot-starter-test test  

Gradle users can add the following dependency in your build.gradle file.

testCompile('org.springframework.boot:spring-boot-starter-test')

Before writing a Test case, we should first build RESTful web services. For further information on building RESTful web services, please refer to the chapter on the same given in this tutorial.

Writing a Unit Test for REST Controller

In this section, let us see how to write a Unit Test for the REST Controller.

First, we need to create Abstract class file used to create web application context by using MockMvc and define the mapToJson() and mapFromJson() methods to convert the Java object into JSON string and convert the JSON string into Java object.

package com.tutorialspoint.demo; import java.io.IOException; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = DemoApplication.class) @WebAppConfiguration public abstract class AbstractTest < protected MockMvc mvc; @Autowired WebApplicationContext webApplicationContext; protected void setUp() < mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); >protected String mapToJson(Object obj) throws JsonProcessingException < ObjectMapper objectMapper = new ObjectMapper(); return objectMapper.writeValueAsString(obj); >protected T mapFromJson(String json, Class clazz) throws JsonParseException, JsonMappingException, IOException < ObjectMapper objectMapper = new ObjectMapper(); return objectMapper.readValue(json, clazz); >>

Next, write a class file that extends the AbstractTest class and write a Unit Test for each method such GET, POST, PUT and DELETE.

Читайте также:  Java send message to thread

The code for GET API Test case is given below. This API is to view the list of products.

@Test public void getProductsList() throws Exception < String uri = "/products"; MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(uri) .accept(MediaType.APPLICATION_JSON_VALUE)).andReturn(); int status = mvcResult.getResponse().getStatus(); assertEquals(200, status); String content = mvcResult.getResponse().getContentAsString(); Product[] productlist = super.mapFromJson(content, Product[].class); assertTrue(productlist.length >0); >

The code for POST API test case is given below. This API is to create a product.

@Test public void createProduct() throws Exception < String uri = "/products"; Product product = new Product(); product.setId("3"); product.setName("Ginger"); String inputJson = super.mapToJson(product); MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.post(uri) .contentType(MediaType.APPLICATION_JSON_VALUE).content(inputJson)).andReturn(); int status = mvcResult.getResponse().getStatus(); assertEquals(201, status); String content = mvcResult.getResponse().getContentAsString(); assertEquals(content, "Product is created successfully"); >

The code for PUT API Test case is given below. This API is to update the existing product.

@Test public void updateProduct() throws Exception < String uri = "/products/2"; Product product = new Product(); product.setName("Lemon"); String inputJson = super.mapToJson(product); MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.put(uri) .contentType(MediaType.APPLICATION_JSON_VALUE).content(inputJson)).andReturn(); int status = mvcResult.getResponse().getStatus(); assertEquals(200, status); String content = mvcResult.getResponse().getContentAsString(); assertEquals(content, "Product is updated successsfully"); >

The code for Delete API Test case is given below. This API will delete the existing product.

@Test public void deleteProduct() throws Exception < String uri = "/products/2"; MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.delete(uri)).andReturn(); int status = mvcResult.getResponse().getStatus(); assertEquals(200, status); String content = mvcResult.getResponse().getContentAsString(); assertEquals(content, "Product is deleted successsfully"); >

The full Controller Test class file is given below −

package com.tutorialspoint.demo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import org.junit.Before; import org.junit.Test; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import com.tutorialspoint.demo.model.Product; public class ProductServiceControllerTest extends AbstractTest < @Override @Before public void setUp() < super.setUp(); >@Test public void getProductsList() throws Exception < String uri = "/products"; MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(uri) .accept(MediaType.APPLICATION_JSON_VALUE)).andReturn(); int status = mvcResult.getResponse().getStatus(); assertEquals(200, status); String content = mvcResult.getResponse().getContentAsString(); Product[] productlist = super.mapFromJson(content, Product[].class); assertTrue(productlist.length >0); > @Test public void createProduct() throws Exception < String uri = "/products"; Product product = new Product(); product.setId("3"); product.setName("Ginger"); String inputJson = super.mapToJson(product); MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.post(uri) .contentType(MediaType.APPLICATION_JSON_VALUE) .content(inputJson)).andReturn(); int status = mvcResult.getResponse().getStatus(); assertEquals(201, status); String content = mvcResult.getResponse().getContentAsString(); assertEquals(content, "Product is created successfully"); >@Test public void updateProduct() throws Exception < String uri = "/products/2"; Product product = new Product(); product.setName("Lemon"); String inputJson = super.mapToJson(product); MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.put(uri) .contentType(MediaType.APPLICATION_JSON_VALUE) .content(inputJson)).andReturn(); int status = mvcResult.getResponse().getStatus(); assertEquals(200, status); String content = mvcResult.getResponse().getContentAsString(); assertEquals(content, "Product is updated successsfully"); >@Test public void deleteProduct() throws Exception < String uri = "/products/2"; MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.delete(uri)).andReturn(); int status = mvcResult.getResponse().getStatus(); assertEquals(200, status); String content = mvcResult.getResponse().getContentAsString(); assertEquals(content, "Product is deleted successsfully"); >>

You can create an executable JAR file, and run the Spring Boot application by using the Maven or Gradle commands given below −

For Maven, you can use the command given below −

Now, you can see the test results in console window.

Test Results in Console Window

For Gradle, you can use the command as shown below −

You can see the rest results in console window as shown below.

Источник

Test a Spring Boot REST Controller with JUnit 5

Learn to unit test given Spring Boot REST controller using Junit 5, Mockito and MockMvc autoconfigured using @WebMvcTest. This technique can be applied to Spring boot as well as Spring MVC applications.

Start by including the latest version of spring-boot-starter-test starter dependency.

 org.springframework.boot spring-boot-starter-test test 

Here is the Spring boot rest controller for which we will be writing unit tests.

  • The controller has a dependency on EmployeeDAO class for persistence.
  • addEmployee() api needs access to the request context using ServletUriComponentsBuilder .
  • addEmployee() api returns HTTP status and header using ResponseEntity class.
@RestController @RequestMapping(path = "/employees") public class EmployeeController < @Autowired private EmployeeDAO employeeDao; @GetMapping(path="/", produces = "application/json") public Employees getEmployees() < return employeeDao.getAllEmployees(); >@PostMapping(path= "/", consumes = "application/json", produces = "application/json") public ResponseEntity addEmployee(@RequestBody Employee employee) < employeeDao.addEmployee(employee); URI location = ServletUriComponentsBuilder.fromCurrentRequest() .path("/") .buildAndExpand(employee.getId()) .toUri(); return ResponseEntity.created(location).build(); > >

2. Unit Testing using Spring Boot @WebMvcTest

The @WebMvcTest annotation is used to unit test the Spring MVC components (@Controller, @ControllerAdvice). It disables the full autoconfiguration and only configures the Spring Security and MockMvc .

@WebMvcTest(EmployeeRESTController.class) public class TestEmployeeRESTController

Finally, use MockMvc bean instance to invoke the APIs and verify the results.

@Test public void getAllEmployeesAPI() throws Exception < mvc.perform(MockMvcRequestBuilders .get("/employees") .accept(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().isOk()) .andExpect(MockMvcResultMatchers.jsonPath("$.employees").exists()) .andExpect(MockMvcResultMatchers.jsonPath("$.employees[*].employeeId").isNotEmpty()); >@Test public void createEmployeeAPI() throws Exception < mvc.perform( MockMvcRequestBuilders .post("/employees") .content(asJsonString(new EmployeeVO(null, "firstName", "lastName", "admin@mail.com"))) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(MockMvcResultMatchers.jsonPath("$.employeeId").exists()); >public static String asJsonString(final Object obj) < try < return new ObjectMapper().writeValueAsString(obj); >catch (Exception e) < throw new RuntimeException(e); >>

3. Unit Testing Spring Controller using Mockito

To use Mockito with Spring, include the following dependency:

  org.mockito mockito-junit-jupiter test 

The test class contains unit tests for the spring boot rest controller using the Mockito APIs. The class:

  • uses @Mock annotation to created mock object for EmployeeDAO dependency.
  • uses @InjectMocks to create EmployeeController class and also inject the mocked employeeDAO instance.
  • MockitoExtension initializes mocks and handles strict stubbings. This extension is the JUnit Jupiter equivalent of our JUnit4 MockitoJUnitRunner.
  • MockHttpServletRequest and RequestContextHolder supply the request context where code under test needs it.
  • Use org.mockito.Mockito.when() and thenReturn() apis to mock the desired behavior.
  • Finally use junit 5 assertions to assert the test results with expected results.
import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import com.howtodoinjava.rest.controller.EmployeeController; import com.howtodoinjava.rest.dao.EmployeeRepository; import com.howtodoinjava.rest.model.Employee; import com.howtodoinjava.rest.model.Employees; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.ResponseEntity; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @ExtendWith(MockitoExtension.class) public class EmployeeControllerTest < @InjectMocks EmployeeController employeeController; @Mock EmployeeDAO employeeDAO; @Test public void testAddEmployee() < MockHttpServletRequest request = new MockHttpServletRequest(); RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); when(employeeDAO.addEmployee(any(Employee.class))).thenReturn(true); Employee employee = new Employee(1, "Lokesh", "Gupta", "howtodoinjava@gmail.com"); ResponseEntityresponseEntity = employeeController.addEmployee(employeeToAdd); assertThat(responseEntity.getStatusCodeValue()).isEqualTo(201); assertThat(responseEntity.getHeaders().getLocation().getPath()).isEqualTo("/1"); > @Test public void testFindAll() < Employee employee1 = new Employee(1, "Lokesh", "Gupta", "howtodoinjava@gmail.com"); Employee employee2 = new Employee(2, "Alex", "Gussin", "example@gmail.com"); Employees employees = new Employees(); employees.setEmployeeList(Arrays.asList(employee1, employee2)); when(employeeDAO.getAllEmployees()).thenReturn(employees); Employees result = employeeController.getEmployees(); assertThat(result.getEmployeeList().size()).isEqualTo(2); assertThat(result.getEmployeeList().get(0).getFirstName()).isEqualTo(employee1.getFirstName()); assertThat(result.getEmployeeList().get(1).getFirstName()).isEqualTo(employee2.getFirstName()); >>

While writing a junit test for a rest controller method, we shall keep in mind that:

  • A unit test is supposed to test only a certain part of code (i.e., code written in the controller class), so we shall mock all the dependencies injected and used in the controller class.
  • If the test utilizes outer dependencies (e.g., database/network), then it is integration testing, not unit testing.
  • We should not use any web server; otherwise, it will slow down unit testing.
  • Each unit test should be independent of other tests.
  • By definition, unit tests should be fast.

In this spring boot rest controller unit testing example with Junit 5 and mockito, we learned to write tests that mock all the controller dependencies and only test the necessary part.

We also learned that we shall not use actual webserver to run the application while unit testing. The server will be needed while integration testing only.

Источник

Оцените статью