Overview:
I am going to test bad request (400) with a customized error message in Spring MVC Test. The test gets 400 as status; however the content body is empty.
The code snippets are as follows:
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
memberServiceController.setMemberDetailsApiController(mockMemberDetailsApiController);
memberServiceController.setResourceMessage(mockResourceMessage);
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.addFilter(new ShallowEtagHeaderFilter())
.apply(documentationConfiguration(restDocumentation))
.build();
}
#Test
public void getMemberDetails_whenStoreIdIsNull_setStatusToBadRequest() throws Exception {
Mockito.doReturn("storeId is empty").when(mockResourceMessage).getMessage(MEMBER_ERROR_INVALID_STOREID);
mockMvc.perform(get(URL)
.header(REQUEST_HEADER_COOKIE, DEFAULT_COOKIE_VALUE)
.param(REQUEST_PARAM_MEMBERSHIP_IDENTIFIER, "MEMBER1"))
.andDo(MockMvcResultHandlers.print())
.andDo(document("memberServices/GetMemberDetailsNullStoreId",
requestHeaders(
headerWithName(REQUEST_HEADER_COOKIE).description(REQUEST_HEADER_COOKIE_DESCRIPTION)
),
requestParameters(
parameterWithName(REQUEST_PARAM_MEMBERSHIP_IDENTIFIER).description(REQUEST_PARAM_MEMBERSHIP_IDENTIFIER_DESCRIPTION)
)))
.andExpect(status().isBadRequest())
.andExpect(content().string(containsString("storeId is empty".toLowerCase())))
.andReturn().getResponse();
}
The raised exception is as follows:
MockHttpServletRequest:
HTTP Method = GET
Request URI = /retailer1/memberDetails
Parameters = {membershipIdentifier=[MEMBER1]}
Headers = {Cookie=[SESSION=67421bc3-36da-4b64-9aca-94edf57211f6]}
Handler:
Type = com.blss.retailServices.memberServices.restServices.MemberRestController
Method = public org.springframework.http.HttpEntity<org.springframework.hateoas.Resource<com.blss.retailServices.memberServices.models.MemberDetailsResponseModel>> com.blss.retailServices.memberServices.restServices.MemberRestController.getMemberDetails(com.blss.retailServices.memberServices.models.MemberModel)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = com.blss.retailServices.InvalidRequestException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 400
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.AssertionError: Response content
Expected: a string containing "storeid is empty"
but: was ""
The generated response in API Doc with asciidoc is as follows:
HTTP/1.1 400 Bad Request
Question:
Now I would appreciate it if anyone can help me find way to get bad request with my customized exception message ("storeId is empty") in order to be added to generated API documentation and have something like bellow as generated response in API documentation:
HTTP/1.1 400 Bad Request,
storeId is empty
The problem was related to exception handling in my code. I forgot to add GlobalControllerExceptionHandler class which is our exception handler to #SpringApplicationConfiguration in my test class. So after adding it as follows my problem solved:
#SpringApplicationConfiguration(classes = {
MemberRestControllerTest.class,
MemberRestController.class,
ResourceResultProcessor.class,
GlobalControllerExceptionHandler.class
})
public class MemberRestControllerTest {
...
}
Related
I have a problem with testing my Spring Boot Controller. I'm trying to test one of Controller put methods, but all I'm getting is Status expected:<200> but was:<400> error message.
My Controller:
#PutMapping(value = "/update", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> updateFields(#RequestBody List<UpdateModel> model) throws JsonProcessingException {
return new ResponseEntity<>(vaultService.updateFields(model), HttpStatus.OK);
}
My ControllerTest:
The URL is ok, and I have a list just like in the get method in my controller.
#Test
void updateFieldsTest() throws Exception {
List<UpdateModel> response = new ArrayList<>();
UpdateModel updateModel = new UpdateModel();
response.add(updateModel);
when(vaultService.updateFields(anyList()))
.thenReturn(String.valueOf(response));
mockMvc.perform(
MockMvcRequestBuilders.put("/api/update")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.accept("application/json"))
.andExpect(MockMvcResultMatchers.status().isOk());
}
And here's an error message:
MockHttpServletRequest:
HTTP Method = PUT
Request URI = /api/update
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Accept:"application/json"]
Body = null
Session Attrs = {}
Handler:
Type = hr.ogcs.ltt.api.lttapi.controller.LttController
Method = hr.ogcs.ltt.api.lttapi.controller.LttController#updateFields(List)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotReadableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 400
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 = []
Status expected:<200> but was:<400>
Expected :200
Actual :400
<Click to see difference>
java.lang.AssertionError: Status expected:<200> but was:<400>
at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:59)
at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:122)
at org.springframework.test.web.servlet.result.StatusResultMatchers.lambda$matcher$9(StatusResultMatchers.java:627)
at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:212)
at hr.ogcs.ltt.api.apicontroller.ControllerTest.updateFieldsTest(ControllerTest.java:113) <31 internal lines>
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)<9 internal lines>
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)<48 internal lines>
I guess it has to be something with sending the required parameter, but I'm not sure what exactly causes this 400 error.
For anyone interested, I've managed to find a solution, so here it is.
#Test
#DisplayName("Test updateFieldsTest")
void updateFieldsTest() throws Exception {
List<UpdateModel> response = new ArrayList<>();
UpdateModel updateModel = new UpdateModel();
response.add(updateModel);
when(vaultService.updateFields(anyList()))
.thenReturn(String.valueOf(response));
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(response);
mockMvc.perform(
MockMvcRequestBuilders.put("/api/update")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(json)
.accept("application/json"))
.andExpect(MockMvcResultMatchers.status().isOk());
}
If you compare this code with the one in my original post, the difference is in adding the ObjectMapper.
You do not have content in the request
mockMvc.perform(
MockMvcRequestBuilders.put("/api/update")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(jsonString)
.accept("application/json"))
.andExpect(MockMvcResultMatchers.status().isOk());
}
I am tring to test my spring-boot rest controller, its giving the login page instead of actual result. its giving 302 status, I have given the controller code , test case and error given by the junit.Please have look all the code and let me you need any other information
Controller
#GetMapping("{userId}/edit/{todoId}")
public ModelAndView updateTodo(#PathVariable Long userId, #PathVariable Long todoId) {
User user = userService.findById(userId);
ModelAndView model = new ModelAndView("todo-form.jsp");
model.addObject("userId",userId);
return model;
}
Junit test
#ExtendWith(SpringExtension.class)
#SpringBootTest
#AutoConfigureMockMvc
class UserApiControllerTest {
#DisplayName("Test updateTodo Api")
#Test
public void updateTodo() throws Exception {
mockMvc.perform(get("/1/edit/2"))
.andExpect(status().isOk());
}
ERROR
MockHttpServletRequest:
HTTP Method = GET
Request URI = /1/edit/2
Parameters = {}
Headers = []
Body = null
Session Attrs = {SPRING_SECURITY_SAVED_REQUEST=DefaultSavedRequest[http://localhost/1/edit/2]}
Handler:
Type = null
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 302
Error message = null
Expires:"0", Location:"http://localhost/login"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = http://localhost/login
Try disabling spring security as it's redirecting to login page.
#AutoConfigureMockMvc(addFilters = false)
secure=false should work too but may be deprecated depending on your version of Spring
I'm pretty new to unit testing with Spring and can't figure out why I can't MockMvc doesn't work with post when the request body is empty. The error I'm getting: java.lang.AssertionError:Response status expected:<200> but was <404>. I've gotten the get endpoint to work correctly so I think the rest controller is wired correctly. I'm just wondering how to write a unit test when a POST or PUT request lacks a body, but has params. I've tried different variations of the below such as chaining .contentType(MediaType.ALL).content(""), but I always get the same error.
UnitTest.java
#Test
public void shouldCreateSomething() throws Exception{
mockMvc.perform(post(somethingUri).param(idParameter, fieldParameter))
.andExpect(status().is(200));
}
RestAPI.java
#Operation(summary = "Updates something", responses = {
#ApiResponse(responseCode = "200", description = "Something was updated"),
#ApiResponse(responseCode = "406", description = "Something was invalid") })
#PutMapping(value = somethingUri)
public void updateSomething(
#Parameter(description = "Something's id", required = true) #RequestParam String id,
#Parameter(description = "Something's name to be updated", required = true) #RequestParam String name)
{
System.out.println("please work");
}
There ended up being 2 problems with my code.
The 404 error was probably caused by an incorrect URI.
Then the ensuing 400 error was caused by my malformed param. It should be:
#Test
public void shouldCreateSomething() throws Exception{
mockMvc.perform(post(somethingUri)
.param(idPropName, idParameter)
.param(fieldPropName,fieldParameter))
.andExpect(status().is(200));
}
So nothing to do with empty body at all
i written a test in spring mockMVC this method:
my method testing is:
#Test
public void getAccount()throws Exception {
mockMvc.perform(get("/account/1"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(view().name("/account/"));
}
and i have a following bug:
MockHttpServletRequest:
HTTP Method = GET
Request URI = /users/1
Parameters = {}
Headers = {}
Body = <no character encoding set>
Session Attrs = {}
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.web.method.annotation.MethodArgumentTypeMismatchException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 500
Error message = null
Headers = {Content-Type=[text/plain;charset=ISO-8859-1], Content-Length=[14]}
Content type = text/plain;charset=ISO-8859-1
Body = We are doomed.
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.AssertionError: Status
Expected :200
Actual :500
This is my POST method test:
what is bad in my test method? what i can fixed this?
I am asking for help and quick answer
It appears you're not looking at the right place for the issue.
The logger outputs that you have an error with the request URI /users/1:
Request URI = /users/1
and your testing method is trying to get /account/1:
mockMvc.perform(get("/account/1"))
As for the error itself, MethodArgumentTypeMismatchException:
Exception that indicates that a method argument has not the expected type.
In other words, the method which is annotated by #GetMapping("/users/{id}") has the wrong #PathVariable parameter type.
In your case, you're using UUID as parameter:
public #ResponseBody ResponseEntity<AccountDTO> getAccount(#PathVariable UUID id) {
However, in your test, you're not passing a UUID, you're passing a numerical value (long/int) in your test.
If you want to generate a random UUID, you can use UUID.randomUUID():
#Test
public void getAccount()throws Exception {
mockMvc.perform(get("/account/" + UUID.randomUUID()))
.andDo(print())
.andExpect(status().isOk())
.andExpect(view().name("/account/"));
}
Alternatively, you could use long instead of uuid in your mapping method:
#GetMapping(value = "/{id}")
#ApiOperation(value = "Retrieve account.")
public #ResponseBody ResponseEntity<AccountDTO> getAccount(#PathVariable Long id) {
return accountService.retreiveById(id).map(ResponseEntity::ok)
.orElseGet(() -> ResponseEntity.notFound().build());
}
though in that case, you'll probably have to change your AccountService.retrieveById(id) method.
Good luck!
I am getting the follow exception org.springframework.web.HttpMediaTypeNotSupportedException trying to test a Json Controller.
The method in controller is:
#RequestMapping(value = "/report", method = RequestMethod.PUT)
public #ResponseBody DatosJsonVO report(#RequestHeader(value = "hash", required = true) String hash,
#RequestBody ReportVO report) {
}
My test method is the following:
#Test
public void reportarPreguntaSesionInvalida() throws Exception {
ReportVO report = new ReportVO();
report.setIdQuestion(1);
report.setIssue("Wrong answer");
mockMvc.perform(put("/json/report").header("hash", "123456789")
.accept(MediaType.APPLICATION_JSON).content(asJsonString(report))).andDo(print())
.andExpect(content().string("{\"code\":2,\"message\":\"Session error\",\"data\":null}"));
}
But, I get this response:
MockHttpServletRequest:
HTTP Method = PUT
Request URI = /json/report
Parameters = {}
Headers = {hash=[8.16615469E8], Accept=[application/json]}
Handler:
Type = com.controller.json.QuestionsJsonController
Async:
Was async started = false
Async result = null
Resolved Exception:
Type = org.springframework.web.HttpMediaTypeNotSupportedException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
MockHttpServletResponse:
Status = 415
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
My Spring Version is 4.0.0.RELEASE
You need to set the content type.
mockMvc.perform(put("/json/report")
.header("hash", "123456789")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(asJsonString(report))).andDo(print())
.andExpect(content().string("{\"code\":2,\"message\":\"Session error\",\"data\":null}"));
mockMvc.perform(put("/json/report").header("hash", "123456789")
.accept(MediaType.APPLICATION_JSON)
.content(asJsonString(report)))
.contentType(MediaType.APPLICATION_JSON)
.andDo(print())
.andExpect(content().string("{\"code\":2,\"message\":\"Session error\",\"data\":null}"));