spring mvc controller Test with Enumeration value - spring

i'm trying to test this Method :
#RequestMapping(value="/PersonalState/{EmployeeId}", method = RequestMethod.PUT)
public #ResponseBody Object Update(#PathVariable Integer EmployeeId, #RequestParam EmployeeState empstate) throws Exception {
EmployeeService.updateEmployeeState(entityManager.find(Employee.class, EmployeeId), empstate);
return null;
}
EmplyeeState is an enumeration , the values are saved in db as integer,this is my test Code:
#Test
public void EmployeeTest() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.put("/PersonalState/{empstate}",EmplyeeState.PERMANENT)
.param("EmployeeId", "550"))
.andDo(print())
.andExpect(MockMvcResultMatchers.status().isOk());
}
I got this Errror:
Resolved Exception:
Type = org.springframework.beans.TypeMismatchException
MockHttpServletResponse:
Status = 400
I tried to pass the two variables as parameters ,passing only the EmployeeId as parameter but i still have the same error besides the param parameters must be both of type String.
Any Idea?

Problem resolved.
i passed as parameter the enum string value.

Related

java.lang.AssertionError: Status : 404

Please Could someone help me , I cant figure out what is the problem, I'am trying to implement a test to this method but it always gives me
java.lang.AssertionError: Status
Expected :200
Actual :400
#PutMapping("/infoUtile/update/{id}")
public Map<String,Object> editActualite(#PathVariable Long id, #Valid #RequestParam ArrayList<Long> idDeleted,
#Valid #RequestParam String content, #Valid #RequestParam String description){
InformationUtile info = this.infoUtileService.getInfoUtileById(id);
info.setContent(content);
info.setDescription(description);
info.setDate(new Date());
if(idDeleted.size() != 0) {
for (int i = 0; i < idDeleted.size(); i++) {
this.mediaService.deleteMedia(idDeleted.get(i));
}
}
InformationUtile i = this.infoUtileService.addOrEditInfoUtile(info);
return getInfoUtileWeb(i);
}
and here is my test that Im trying to implement
#Test
public void update() throws Exception {
InformationUtile informationUtile = new InformationUtile();
informationUtile.setId(1);
informationUtile.setContent("oumaima");
informationUtile.setDescription("test");
Media medias = new Media();
medias.setId(1);
medias.setType("image/png");
medias.setUrl("C:\\files\\actualite\\32769\\adobexd.png");
List<Media> allMedias = new ArrayList<Media>();
allMedias.add(medias);
informationUtile.setMedias(allMedias);
User user = new User();
user.setId(1);
user.setNom("oumaima");
informationUtile.setUser(user);
ArrayList<Long> idDeleted = new ArrayList<>();
idDeleted.add(0L);
Mockito.when(informationUtileService.getInfoUtileById(Mockito.<Long>any())).thenReturn(new InformationUtile());
Mockito.when(informationUtileService.addOrEditInfoUtile(Mockito.any(InformationUtile .class))).thenReturn(informationUtile);
mockMvc.perform(put("/infoUtile/update/{id}",informationUtile.getId()).requestAttr("idDeleted",idDeleted)
.param("content",informationUtile.getContent())
.param("description",informationUtile.getDescription())
)
.andExpect(status().isOk());
verify(informationUtileService, times(1)).getInfoUtileById(informationUtile.getId());
verify(informationUtileService, times(1)).addOrEditInfoUtile(informationUtile);
verifyNoMoreInteractions(informationUtileService);
}
You are defining three request parameters at your endpoint #Valid #RequestParam ArrayList<Long> idDeleted, #Valid #RequestParam String content, #Valid #RequestParam String description which means they are query parameters after the url, e.g. http://localhost:8080/?idDeleted=1&idDeleted=2&content=Hello&description=Duke.
The HTTP 404 indicates that Spring could not find a handler for your request, meaning the client (in your case MockMvc) has a malformed URL.
In your current MockMvc request setup you are using .requestAttr() for the idDeleted request parameter.
All of them should be .param():
mockMvc
.perform(put("/infoUtile/update/{id}",informationUtile.getId())
.param("idDeleted", idDeletedOne , idDeletedTwo)
.param("content",informationUtile.getContent())
.param("description",informationUtile.getDescription())
)
PS: I guess the #Valid annotations are redundant/not needed here as you are not checking e.g. payload which has Bean Validation annotations to verify the content.
UPDATE: .param() is overloaded with .parm(String name, String... values), so you can pass your list of idDeleted with either .param("idDeleted", idDeletedOne, idDeletedTwo) or you can pass a String[] with all your Long values represented as a String

Calling #PatchMapping annotated method using TestRestController

I have a method annotated with #PatchMapping.
#PatchMapping(path= "/api/transaction/{transaction-id}/return")
public ResponseEntity<Transaction> returnBookTransaction(#PathVariable(name="transaction-id") Long transactionId){
Transaction transaction = transactionRepository.findById(transactionId).get();
transaction.setDateOfReturn(LocalDateTime.now());
return ResponseEntity.ok().body(transaction);
}
I need to test this method. In test method, I need to use TestRestController.patchForObject();
#Test
public void testReturnBookTransaction() throws Exception {
ResponseEntity<Transaction> response = testRestTemplate.patchForObject("/api/transaction/{transaction-id}/return",
, Transaction.class, 1);
Assert.assertEquals(200, response.getStatusCode().value());
}
The code above shows compiler error in template.patchForObject(), as it needs 4 parameters. I don't know, what to pass in 4th parameter.
I just have to pass 1 for {transaction-id}, URL String and return type class, which I have already passed. But this method requires one more parameter as Object.
I don't know, what to pass in Object.
From Spring's documentation:
public T patchForObject(String url,
Object request,
Class responseType,
Map uriVariables)
throws RestClientException
The second argument is for request object. Since you don't have any request-related info in your controller method, you can set it to null.
Try the following:
Transaction response = testRestTemplate.patchForObject("/api/transaction/{transaction-id}/return", null
, Transaction.class, 1);
UPDATE:
If you want to have access to the response entity, try the following:
ResponseEntity<Transaction> response = testRestTemplate.exchange("/api/transaction/{transaction-id}/return", HttpMethod.PATCH, null,Transaction.class, 1);

mockMVC method GET java.lang.AssertionError: Status Expected :200 Actual :500

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!

How test Post request with custom object in content type application/x-www-form-urlencoded?

I have controller:
#PostMapping(value = "/value/", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String updateSettings(final Dto dto) {
System.out.println(">>> " + dto);
return "template";
}
Controller works if I send request across chrome window. But when I write test for this method I get problem. Not converted object, value not inserted.
Test:
#Test
#WithMockUser(username = FAKE_VALID_USER, password = FAKE_VALID_PASSWORD)
public void test_B_CreateDtoWithValidForm() throws Exception {
final Dto dto = new Dto();
dto.setId("value");
dto.setEnabled("true");
this.mockMvc.perform(post(URL_SET_PROVIDER_SETTINGS)
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.content(dto.toString()))
.andDo(print());
}
Output is >>> Dto{id=null, enabled=false}
How test Post request with custom object in content type application/x-www-form-urlencoded?
In this case you don't need to use content, but instead you need to use param in this way:
this.mockMvc.perform(post(URL_SET_PROVIDER_SETTINGS)
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("id", "value")
.param("enabled", "true"))
.andDo(print());

Empty Exception Body in Spring MVC Test

I am having trouble while trying to make MockMvc to include the exception message in the response body. I have a controller as follows:
#RequestMapping("/user/new")
public AbstractResponse create(#Valid NewUserParameters params, BindingResult bindingResult) {
if (bindingResult.hasErrors()) throw BadRequestException.of(bindingResult);
// ...
}
where BadRequestException looks sth like this:
#ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "bad request")
public class BadRequestException extends IllegalArgumentException {
public BadRequestException(String cause) { super(cause); }
public static BadRequestException of(BindingResult bindingResult) { /* ... */ }
}
And I run the following test against /user/new controller:
#Test
public void testUserNew() throws Exception {
getMockMvc().perform(post("/user/new")
.param("username", username)
.param("password", password))
.andDo(print())
.andExpect(status().isOk());
}
which prints the following output:
Resolved Exception:
Type = controller.exception.BadRequestException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
MockHttpServletResponse:
Status = 400
Error message = bad request
Headers = {X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-Frame-Options=[DENY]}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Does anybody have an idea on why is Body missing in the print() output?
Edit: I am not using any custom exception handlers and the code works as expected when I run the server. That is, running the application and making the same request to the server returns back
{"timestamp":1423076185822,
"status":400,
"error":"Bad Request",
"exception":"controller.exception.BadRequestException",
"message":"binding failed for field(s): password, username, username",
"path":"/user/new"}
as expected. Hence, there is a problem with the MockMvc I suppose. It somehow misses to capture the message field of the exception, whereas the default exception handler of the regular application server works as expected.
After opening a ticket for the issue, I was told that the error message in the body is taken care of by Spring Boot which configures error mappings at the Servlet container level and since Spring MVC Test runs with a mock Servlet request/response, there is no such error mapping. Further, they recommended me to create at least one #WebIntegrationTest and stick to Spring MVC Test for my controller logic.
Eventually, I decided to go with my own custom exception handler and stick to MockMvc for the rest as before.
#ControllerAdvice
public class CustomExceptionHandler {
#ExceptionHandler(Throwable.class)
public #ResponseBody
ExceptionResponse handle(HttpServletResponse response, Throwable throwable) {
HttpStatus status = Optional
.ofNullable(AnnotationUtils.getAnnotation(throwable.getClass(), ResponseStatus.class))
.map(ResponseStatus::value)
.orElse(HttpStatus.INTERNAL_SERVER_ERROR);
response.setStatus(status.value());
return new ExceptionResponse(throwable.getMessage());
}
}
#Data
public class ExceptionResponse extends AbstractResponse {
private final long timestamp = System.currentTimeMillis();
private final String message;
#JsonCreator
public ExceptionResponse(String message) {
checkNotNull(message, "message == NULL");
this.message = message;
}
}
This likely means that you either didn't handle the exception or you've really left the body empty. To handle the exception either add an error handler in the controller
#ExceptionHandler
public #ResponseBody String handle(BadRequestException e) {
return "I'm the body";
}
or user the global error handler if you're on 3.2 or above
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler
public #ResponseBody String handleBadRequestException(BadRequestException ex) {
return "I'm the body";
}
}
with this the body will be populate, you should populate it with your error message
Updated solution:
If you don't want to do a full integration test but still want to make sure the message is as expected, you can still do the following:
String errorMessage = getMockMvc()
.perform(post("/user/new"))
...
.andReturn().getResolvedException().getMessage();
assertThat(errorMessage, is("This is the error message!");

Resources