How to write a unit test for a Spring Boot Controller endpoint

JavaUnit TestingJunitSpring BootSpring Test-Mvc

Java Problem Overview


I have a sample Spring Boot app with the following

Boot main class

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

Controller

@RestController
@EnableAutoConfiguration
public class HelloWorld {
    @RequestMapping("/")
    String gethelloWorld() {
        return "Hello World!";
    }

}

What's the easiest way to write a unit test for the controller? I tried the following but it complains about failing to autowire WebApplicationContext

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = DemoApplication.class)
public class DemoApplicationTests {
	
	final String BASE_URL = "http://localhost:8080/";

	@Autowired
	private WebApplicationContext wac;

	private MockMvc mockMvc;

	@Before
	public void setup() {
	    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
	}
	     
	@Test
	public void testSayHelloWorld() throws Exception{
		
		 this.mockMvc.perform(get("/")
                 .accept(MediaType.parseMediaType("application/json;charset=UTF-8")))
                 .andExpect(status().isOk())
                 .andExpect(content().contentType("application/json"));
	}

	@Test
	public void contextLoads() {
	}

}

Java Solutions


Solution 1 - Java

Spring MVC offers a standaloneSetup that supports testing relatively simple controllers, without the need of context.

> Build a MockMvc by registering one or more @Controller's instances and > configuring Spring MVC infrastructure programmatically. This allows > full control over the instantiation and initialization of controllers, > and their dependencies, similar to plain unit tests while also making > it possible to test one controller at a time.

An example test for your controller can be something as simple as

public class DemoApplicationTests {

    private MockMvc mockMvc;

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.standaloneSetup(new HelloWorld()).build();
    }

    @Test
    public void testSayHelloWorld() throws Exception {
        this.mockMvc.perform(get("/")
           .accept(MediaType.parseMediaType("application/json;charset=UTF-8")))
           .andExpect(status().isOk())
           .andExpect(content().contentType("application/json"));

    }
}

Solution 2 - Java

The new testing improvements that debuted in Spring Boot 1.4.M2 can help reduce the amount of code you need to write situation such as these.

The test would look like so:

import static org.springframework.test.web.servlet.request.MockMvcRequestB‌​uilders.get; 
import static org.springframework.test.web.servlet.result.MockMvcResultMat‌​chers.content; 
import static org.springframework.test.web.servlet.result.MockMvcResultMat‌​chers.status;

@RunWith(SpringRunner.class)
@WebMvcTest(HelloWorld.class)
public class UserVehicleControllerTests {
    
    @Autowired
    private MockMvc mockMvc;
    
    @Test
    public void testSayHelloWorld() throws Exception {
        this.mockMvc.perform(get("/").accept(MediaType.parseMediaType("application/json;charset=UTF-8")))
                .andExpect(status().isOk())
                .andExpect(content().contentType("application/json"));
    }
    
}

See this blog post for more details as well as the documentation

Solution 3 - Java

Here is another answer using Spring MVC's standaloneSetup. Using this way you can either autowire the controller class or Mock it.

    import static org.mockito.Mockito.mock;
    import static org.springframework.test.web.server.request.MockMvcRequestBuilders.get;
    import static org.springframework.test.web.server.result.MockMvcResultMatchers.content;
    import static org.springframework.test.web.server.result.MockMvcResultMatchers.status;
    
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.http.MediaType;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.springframework.test.web.server.MockMvc;
    import org.springframework.test.web.server.setup.MockMvcBuilders;
    
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class DemoApplicationTests {
    	
    	final String BASE_URL = "http://localhost:8080/";
    
    	@Autowired
        private HelloWorld controllerToTest;
    
        private MockMvc mockMvc;
    
        @Before
        public void setup() {
            this.mockMvc = MockMvcBuilders.standaloneSetup(controllerToTest).build();
        }
    
        @Test
        public void testSayHelloWorld() throws Exception{
        	//Mocking Controller
        	controllerToTest = mock(HelloWorld.class);
    
             this.mockMvc.perform(get("/")
                     .accept(MediaType.parseMediaType("application/json;charset=UTF-8")))
                     .andExpect(status().isOk())
                     .andExpect(content().mimeType(MediaType.APPLICATION_JSON));
        }
    
        @Test
        public void contextLoads() {
        }
    
    }

Solution 4 - Java

Adding @WebAppConfiguration (org.springframework.test.context.web.WebAppConfiguration) annotation to your DemoApplicationTests class will work.

Solution 5 - Java

Let assume i am having a RestController with GET/POST/PUT/DELETE operations and i have to write unit test using spring boot.I will just share code of RestController class and respective unit test.Wont be sharing any other related code to the controller ,can have assumption on that.

@RestController
@RequestMapping(value = “/myapi/myApp” , produces = {"application/json"})
public class AppController {

	
	@Autowired
	private AppService service;

	@GetMapping
	public MyAppResponse<AppEntity> get() throws Exception {
		
		MyAppResponse<AppEntity> response = new MyAppResponse<AppEntity>();
		service.getApp().stream().forEach(x -> response.addData(x));	
		return response;
	}
	
	
	@PostMapping
	public ResponseEntity<HttpStatus> create(@RequestBody AppRequest request) throws Exception {
		//Validation code		
		service.createApp(request);
		
	    return ResponseEntity.ok(HttpStatus.OK);
	}
	
	@PutMapping
	public ResponseEntity<HttpStatus> update(@RequestBody IDMSRequest request) throws Exception {
		
		//Validation code
		service.updateApp(request);
		
	    return ResponseEntity.ok(HttpStatus.OK);
	}
	
	@DeleteMapping
	public ResponseEntity<HttpStatus> delete(@RequestBody AppRequest request) throws Exception {
		
		//Validation		
		service.deleteApp(request.id);
		
	    return ResponseEntity.ok(HttpStatus.OK);
	}

}

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Main.class)
@WebAppConfiguration
public abstract class BaseTest {
   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> T mapFromJson(String json, Class<T> clazz)
      throws JsonParseException, JsonMappingException, IOException {
      
      ObjectMapper objectMapper = new ObjectMapper();
      return objectMapper.readValue(json, clazz);
   }
}

public class AppControllerTest extends BaseTest {
	
	@MockBean
	private IIdmsService service;
	
	private static final String URI = "/myapi/myApp";
	

   @Override
   @Before
   public void setUp() {
      super.setUp();
   }
  
   @Test
   public void testGet() throws Exception {
	   AppEntity entity = new AppEntity();
      List<AppEntity> dataList = new ArrayList<AppEntity>();
      AppResponse<AppEntity> dataResponse = new AppResponse<AppEntity>();
      entity.setId(1);
      entity.setCreated_at("2020-02-21 17:01:38.717863");
      entity.setCreated_by(“Abhinav Kr”);
      entity.setModified_at("2020-02-24 17:01:38.717863");
      entity.setModified_by(“Jyoti”);
            dataList.add(entity);
      
      dataResponse.setData(dataList);
      
      Mockito.when(service.getApp()).thenReturn(dataList);
      
      RequestBuilder requestBuilder =  MockMvcRequestBuilders.get(URI)
				 .accept(MediaType.APPLICATION_JSON);

      	MvcResult mvcResult = mvc.perform(requestBuilder).andReturn();
      	MockHttpServletResponse response = mvcResult.getResponse();
      	
      	String expectedJson = this.mapToJson(dataResponse);
      	String outputInJson = mvcResult.getResponse().getContentAsString();
      	
      	assertEquals(HttpStatus.OK.value(), response.getStatus());
      	assertEquals(expectedJson, outputInJson);
   }
  
   @Test
   public void testCreate() throws Exception {
	  
	   AppRequest request = new AppRequest();
	   request.createdBy = 1;
	   request.AppFullName = “My App”;
	   request.appTimezone = “India”;
	   
	   String inputInJson = this.mapToJson(request);
	   Mockito.doNothing().when(service).createApp(Mockito.any(AppRequest.class));
	   service.createApp(request);
	   Mockito.verify(service, Mockito.times(1)).createApp(request);
	   
	   RequestBuilder requestBuilder =  MockMvcRequestBuilders.post(URI)
			  												 .accept(MediaType.APPLICATION_JSON).content(inputInJson)
			  								                 .contentType(MediaType.APPLICATION_JSON);
	   
	   MvcResult mvcResult = mvc.perform(requestBuilder).andReturn();
	   MockHttpServletResponse response = mvcResult.getResponse();
	   assertEquals(HttpStatus.OK.value(), response.getStatus());
	     
   }
  
   @Test
   public void testUpdate() throws Exception {
	   
	   AppRequest request = new AppRequest();
	   request.id = 1;
	   request.modifiedBy = 1;
	    request.AppFullName = “My App”;
	   request.appTimezone = “Bharat”;
	   
	   String inputInJson = this.mapToJson(request);
	   Mockito.doNothing().when(service).updateApp(Mockito.any(AppRequest.class));
	   service.updateApp(request);
	   Mockito.verify(service, Mockito.times(1)).updateApp(request);
	   
	   RequestBuilder requestBuilder =  MockMvcRequestBuilders.put(URI)
			  												 .accept(MediaType.APPLICATION_JSON).content(inputInJson)
			  								                 .contentType(MediaType.APPLICATION_JSON);
	   
	   MvcResult mvcResult = mvc.perform(requestBuilder).andReturn();
	   MockHttpServletResponse response = mvcResult.getResponse();
	   assertEquals(HttpStatus.OK.value(), response.getStatus());
	     
   }
   
   @Test
   public void testDelete() throws Exception {
	   
	   AppRequest request = new AppRequest();
	   request.id = 1;
	   
	   String inputInJson = this.mapToJson(request);
	   Mockito.doNothing().when(service).deleteApp(Mockito.any(Integer.class));
	   service.deleteApp(request.id);
	   Mockito.verify(service, Mockito.times(1)).deleteApp(request.id);
	   
	   RequestBuilder requestBuilder =  MockMvcRequestBuilders.delete(URI)
			  												 .accept(MediaType.APPLICATION_JSON).content(inputInJson)
			  								                 .contentType(MediaType.APPLICATION_JSON);
	   
	   MvcResult mvcResult = mvc.perform(requestBuilder).andReturn();
	   MockHttpServletResponse response = mvcResult.getResponse();
	   assertEquals(HttpStatus.OK.value(), response.getStatus());
	     
   }
   
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser6123723View Question on Stackoverflow
Solution 1 - JavaMaster SlaveView Answer on Stackoverflow
Solution 2 - JavageoandView Answer on Stackoverflow
Solution 3 - JavaSiva KumarView Answer on Stackoverflow
Solution 4 - JavaCharles WangView Answer on Stackoverflow
Solution 5 - Javaabhinav kumarView Answer on Stackoverflow