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

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() {
}


}
126186 次浏览

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"));


}
}

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

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

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() {
}


}

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());


}


}