Reputation: 973
How do i validate/test the 500 internal server error in MockMvc, when my controller is of Async servlet nature?
I am writing unit test cases for my REST endpoint as part of a test cases i need to validate that the server sends 500 internal error as http code and with appropriate error message.
Here is my spring boot based app: (all imports are omitted for better readability)
@RestController
@RequestMapping("/user")
@EnableAutoConfiguration
@SpringBootApplication
public class App
{
@RequestMapping(method = RequestMethod.GET, value = "/{name}",
produces = MediaType.APPLICATION_JSON_VALUE)
private DeferredResult<String> greetByJson(@PathVariable("name") final String name){
DeferredResult<String> dResult = new DeferredResult<String>();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
dResult.setErrorResult(new RuntimeException("Boom!!! time for Internal server error"));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
return dResult;
}
public static void main( String[] args )
{
SpringApplication.run(App.class);
}
}
Here is my MovkMvc JUnit test cases:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MockServletContext.class)
@WebAppConfiguration
public class AppTest {
private final MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new App())
.build();
@Test
public void testAsyncInternalServerError() {
try {
MvcResult mvcResult = mockMvc.perform(
get("/user/naveen").accept(MediaType.APPLICATION_JSON_VALUE))
.andExpect(request().asyncStarted())
.andReturn();
System.out.println("Http Response Content = " + mvcResult.getAsyncResult());
System.out.println("Http Response Status Code = " + mvcResult.getResponse().getStatus());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Below is the console prints:
2015-08-08 18:11:51.494 INFO 10224 --- [ main] o.s.w.c.s.GenericWebApplicationContext : Refreshing org.springframework.web.context.support.GenericWebApplicationContext@a82c5f1: startup date [Sat Aug 08 18:11:51 IST 2015]; root of context hierarchy
2015-08-08 18:11:51.526 INFO 10224 --- [ main] o.e.j.i.junit.runner.RemoteTestRunner : Started RemoteTestRunner in 0.258 seconds (JVM running for 1.131)
Http Response Content = java.lang.RuntimeException: Boom!!! time for Internal server error
Http Response Status Code = 200
2015-08-08 18:11:56.584 INFO 10224 --- [ Thread-1] o.s.w.c.s.GenericWebApplicationContext : Closing org.springframework.web.context.support.GenericWebApplicationContext@a82c5f1: startup date [Sat Aug 08 18:11:51 IST 2015]; root of context hierarchy
From the above log its evident that MockMvc returns http status code as 200 not 500. The error message is just fine.
Where as when i invoke the end point using Chrome postman, i see 500 internal server error as attached in the image
Upvotes: 6
Views: 10174
Reputation: 31232
Following is working example(Groovy Spock specification), with a method asyncPerform(builder)
import static org.hamcrest.core.IsNull.notNullValue
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*
@ContextConfiguration(classes = [MyConfig])
@WebAppConfiguration
class MyControllerComponentSpec extends Specification {
@Autowired
WebApplicationContext webApplicationContext
MockMvc endpoint
def setup() {
endpoint = MockMvcBuilders.webAppContextSetup(webApplicationContext).build()
}
ResultActions asyncPerform(MockHttpServletRequestBuilder builder) throws Exception {
ResultActions resultActions = endpoint.perform(builder);
asyncDispatch(resultActions.andExpect(request()
.asyncResult(notNullValue()))
.andReturn()));
}
def "accepts valid request and responds with 200 status code and response body"() {
when:
def response = asyncPerform(post("/my_async_endpoint")
.content("""{"correlationID": "fe5d1699-20e3-4502-bf51-b947e6b9e51a"}""")
.header("Content-Type", "application/json"))
.andDo(print())
then:
response.andExpect(status().is(200))
.andExpect(jsonPath("body.correlationID").value("fe5d1699-20e3-4502-bf51-b947e6b9e51a"))
}
}
Upvotes: 0
Reputation: 141
Custom perform
helper method that handles both sync and async request:
ResultActions perform(MockHttpServletRequestBuilder builder) throws Exception {
ResultActions resultActions = mockMvc.perform(builder);
if (resultActions.andReturn().getRequest().isAsyncStarted()) {
return mockMvc.perform(asyncDispatch(resultActions
.andExpect(request().asyncResult(anything()))
.andReturn()));
} else {
return resultActions;
}
}
Longer answer with example here
Upvotes: 3
Reputation: 3355
You must perform async dispatch and test the status afterwards:
@Test
public void testMethod() throws Exception {
MvcResult mvcResult = mockMvc.perform(get("/your/endpoint"))
.andExpect(request().asyncStarted())
.andExpect(request().asyncResult(notNullValue()))
.andReturn();
mockMvc.perform(asyncDispatch(mvcResult))
.andExpect(status().isInternalServerError())
.andReturn();
}
Upvotes: 15