Mock Mvc unit test throws Http 406 - spring-boot

My controller has this code
#RequestMapping(value = "/getUserByEmailId/{emailId}", method = GET, produces = "application/json")
public ResponseEntity<UserRegistrationResponse> getUserByEmailId(#PathVariable String emailId) throws ServiceException {
return new ResponseEntity(generateUserOutput(userService.findUserByEmail(emailId)), OK);
}
My test has this:
#Test
public void testGetUserByEmailId() throws Exception {
when(userService.findUserByEmail(any())).thenReturn(userOutput);
MvcResult mvcResult = performGet("/user/getUserByEmailId/{emailId}", userRegistrationRequest.getEmail());
assertEquals(200, mvcResult.getResponse().getStatus());
}
private MvcResult performGet(String path, String pathVariable) throws Exception {
return mockMvc.perform(get(path,pathVariable)
.accept(APPLICATION_JSON))
.andDo(print())
.andReturn();
}
This is returning HTTP status code 406.
MockHttpServletRequest:
HTTP Method = GET
Request URI = /user/getUserByEmailId/testEmailId#test.com
Parameters = {}
Headers = {Accept=[application/json]}
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = com.nz.unicorn.web.controller.UserController
Method = public org.springframework.http.ResponseEntity<com.nz.unicorn.web.response.UserRegistrationResponse> com.nz.unicorn.web.controller.UserController.getUserByEmailId(java.lang.String) throws com.nz.unicorn.service.exception.ServiceException
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.web.HttpMediaTypeNotAcceptableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 406
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Is there anything I can do? I have seen a lot of questions without an answer that I can use. Do note, mine is not a spring test, it's a unit test.

This is a workaround I did, changed the controller as follows and it works:
#RequestMapping(value = "/getByEmailId/{emailId}", method = RequestMethod.GET, produces = "application/json")
#ResponseStatus(OK)
public UserRegistrationResponse getUserByEmailId(#PathVariable String emailId) throws ServiceException {
return generateUserOutput(userService.findUserByEmail(emailId));
}
This solved, don't know why ResponseEntity still didn't work though.

Related

unable to get the response entity from an api with mockmvc

I have an issue when I try to run my controller's unit test class. I get always a empty body in the response and I don't manage to find why.
I put here the code. Maybe someone with an external vision will be able to see the reason.
the controller:
#ResponseBody
#PostMapping(path = "/upload", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<Object> uploadFile(#RequestParam("file") MultipartFile multipartFileData, #RequestParam(name="jobId", required = false) String jobId) {
JobStatus result;
try {
result = this.fileService.uploadFileChunk(multipartFileData, 1, 1, jobId);
}catch (ExecutionException|InterruptedException|IOException ex){
Thread.currentThread().interrupt();
return new ResponseEntity<>(ex,HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<>(result,HttpStatus.OK);
}
the unit test class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes= FileUploadServiceRestController.class)
public class FileUploadServiceControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#MockBean
private FileUploadServiceImpl fileService;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
#Test
public void testUploadFile()
throws Exception {
MockMultipartFile file
= new MockMultipartFile(
"file",
"hello.txt",
MediaType.TEXT_PLAIN_VALUE,
"Hello, World!".getBytes()
);
JobStatus job = new JobStatus("uuid", ConstantUtil.JOB_STARTED);
when(fileService.uploadFileChunk(Mockito.any(MultipartFile.class),Mockito.eq(1),Mockito.eq(1),Mockito.isNull())).thenReturn(job);
mockMvc.perform(MockMvcRequestBuilders.multipart("/file/upload").file(file))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk());
}
}
and the object which will be transfered:
public class JobStatus implements Serializable {
private static final long serialVersionUID = -4405865740177389860L;
private String jobId;
private String status;
public JobStatus() {
}
public JobStatus(String jobId, String status) {
this.jobId = jobId;
this.status = status;
}
public String getJobId() {
return jobId;
}
public void setJobId(String jobId) {
this.jobId = jobId;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
For information, this controller works well when I call it from the client. I can see that the mock is well returned when I put a breakpoint at the end of the controller, but the response body stay empty.
I add here the result of print if it could help:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /file/upload
Parameters = {}
Headers = [Content-Type:"multipart/form-data"]
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = org.iso.fileservice.controller.FileUploadServiceRestController
Method = org.iso.fileservice.controller.FileUploadServiceRestController#uploadFile(MultipartFile, String)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotWritableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 500
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
thanks, Mathieu
Just ran into this today.
I found the answer on Why MockMvc request retrieve empty responseBody while test succeed?
I just added My restController with #Autowired instead of #InjectMocks, after that I started to retrieve the Response Entity instead of a 500 status response

How to junit test a method with Mono return type

What is the proper way to junit mono and get a body response?
I expected "Single Item Only" to be in the body response of MockHttpServletResponse. But instead I see it's returned in Async.
Sample Method:
public Mono<String> getData(final String data) {
return this.webClient.get().uri("/dataValue/" + data)
.retrieve()
.bodyToMono(String.class);
}
Sample Junit:
#WebMvcTest(DataController.class)
public class DataMockTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private Service service;
#Test
public void getDataTest() throws Exception {
Mono<String> strMono = Mono.just("Single Item Only");
when(service.getData("DATA")).thenReturn(strMono);
this.mockMvc.perform(get("/some/rest/toget/data")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status()
.isOk());
}
Test Response:
MockHttpServletRequest:
HTTP Method = GET
Request URI = /some/rest/toget/data
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Accept:"application/json"]
Body = null
Session Attrs = {}
Async:
Async started = true
Async result = Single Item Only
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Updated Junit with StepVerifier:
#WebMvcTest(DataController.class)
public class DataMockTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private Service service;
#Test
public void getDataTest() throws Exception {
Mono<String> strMono = Mono.just("Single Item Only");
when(service.getData("DATA")).thenReturn(strMono);
//Added this which verifies the mono method. How to mock it so the API would return the mono value?
StepVerifier.create(service.getDATA("DATA"))
.assertNext(data -> {
assertNotNull(data);
assertEquals("Single Item Only", data);
}).verifyComplete();
this.mockMvc.perform(get("/some/rest/toget/data")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status()
.isOk());
}
It seems to me that what you are looking forward is StepVerifier. With it you cam assert the content of Mono an Flux data types. Please refer to https://www.baeldung.com/reactive-streams-step-verifier-test-publisher.

#WebMvcTest - How to configure security so the test can run?

I am trying to unit test my Spring Controller service that returns a String. I want to assert that the expected url name is correct but my test always returns a null url. I am using SpringRunner, #WebMvcTest and MockMvc for this.
#Slf4j
#Controller
public class CompanyInfoController {
private CompanyService companyService;
#Autowired
public CompanyInfoController(final CompanyService companyService) {
this.companyService = companyService;
}
#PreAuthorize("#someService.hasRole('" + Constants.MY_ROLE + "')")
#RequestMapping(value = "/companyInfo", method = RequestMethod.GET)
public String getCompanyInfo(final HttpServletRequest request, final
Model model) throws Exception {
log.debug("Getting Company Info");
final CompanyInfoDTO companyInformation = loadCompanyInfo(request);
model.addAttribute("companyInformation", companyInformation);
return "companyInfo";
}
private CompanyInfoDTO loadCompanyInfo(final HttpServletRequest request) throws Exception {
final Account someAccount= (Account)request.getAttribute("someAccount");
if (null != someAccount) {
final CompanyInfoDTO companyInformation = companyService.getCompanyInfo(someAccount);
return companyInformation;
} else {
throw new Exception("Unable to retrieve this account details.");
}
}
}
Controller Test -
#RunWith(SpringRunner.class)
#WebMvcTest(CompanyInfoController.class)
#Import(SecurityConfiguration.class)
public class CompanyInfoControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private CompanyService companyService;
#MockBean(name = "someService")
private SomeService someAuthorizationService;
#Before
public void setUp(){
Mockito.when(someAuthorizationService.hasRole(Constants.MY_ROLE))
.thenReturn(true);
}
#Test
#WithMockUser
public void canInvokeGetCompanyInfoServiceSuccessfully() throws Exception{
CompanyInfoDTO dto = new CompanyInfoDTO();
final Account mockAccount = new Account();
mockAccount.setId("12334");
Mockito.when(companyService.getCompanyInfo(Mockito.any(Account.class)))
.thenReturn(dto);
RequestBuilder request = get("/companyInfo")
.requestAttr("someAccount", mockAccount);
mockMvc.perform(get("/companyInfo"))
.andExpect(status().isOk());
}
#Test
#WithMockUser
public void forwardsToCompanyInfoPageSuccessfully() throws Exception{
CompanyInfoDTO dto = new CompanyInfoDTO();
final Account mockAccount = new Account();
mockAccount.setId("12334");
Mockito.when(companyService.getCompanyInfo(Mockito.any(Account.class)))
.thenReturn(dto);
RequestBuilder request = get("/companyInfo")
.requestAttr("someAccount", mockAccount);
mockMvc.perform(request)
.andExpect(redirectedUrl("companyInfo"));
}
}
The test keeps failing with this error -
java.lang.AssertionError: Redirected URL
Expected :companyInfo
Actual :null
Not sure what I am missing!
I do not see the debug statement inside my controller getting printed. But my controller is not mocked out. Is my security mocking correct? Any help is greatly appreciated.
Update - This is what I see in my logs -
MockHttpServletRequest:
HTTP Method = GET
Request URI = /companyInfo
Parameters = {}
Headers = []
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = org.cmt.controller.CompanyInfoController
Method = public java.lang.String org.cmt.controller.CompanyInfoController.getCompanyInfo(javax.servlet.http.HttpServletRequest,org.springframework.ui.Model) throws java.lang.Exception
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", X-Frame-Options:"SAMEORIGIN"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.AssertionError: No ModelAndView found
at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:36)
I see Http response code 200. I am assuming it authenticates just fine. But why would my model and view be null?

Spring boot throws HttpMediaTypeNotAcceptableException: Could not find acceptable representation

Here are the 2 mappings I have in my class:
#Override
#RequestMapping(value="/getUserDetails", consumes={"application/xml", "text/xml"}, produces={"application/xml", "text/xml"}, method = RequestMethod.POST)
#ResponseBody
#ApiOperation(httpMethod = "GET", value = "Response for user details", notes = "Gets response for user details", response = GetUserDetailsResponse.class )
#ApiResponse(code = 200, message = "returns response for user details")
#Timed(name = "http.userservice.getUserDetails", absolute = true)
public GetUserDetailsResponse getUserDetails(
#RequestBody GetUserDetailsRequest request) throws ServiceException {
GetUserDetailsResponse response = new GetUserDetailsResponse();
String username = request.getUsername();
User user = createUser(username);
response.setUser(user);
response.setUsername(username);
return response;
}
#Override
#RequestMapping(value = "/", method = RequestMethod.GET)
#ResponseBody
#ApiOperation(value = "hello world" , response = String.class )
#ApiResponse(code = 200, message = "hello world")
public String helloWorld() {
return "hello world";
}
When I request localhost:8080/,
I am getting proper response i.e. "hello world". "/" is mapped to second method below.
But when I request for localhost:8080/getUserDetails with POST request, spring throws HttpMediaTypeNotAcceptableException.
Any idea?
Below is the xml data i am sending as part of POST request
<userDetailsRequest>
    <username>abc#cdk.com</username>
</userDetailsRequest>

400 (Bad Request) while sending json in Spring

I'm trying to send json string to Spring controller, i'm getting 400 - bad request as response
i'm using Spring 4.0.3
This is my controller
#Controller
public class Customer{
#RequestMapping(value = "/apis/test", method = RequestMethod.GET, produces = "application/json")
public #ResponseBody String test(HttpServletRequest params) throws JsonIOException {
String json = params.getParameter("json");
JsonParser jObj = new JsonParser();
JsonArray jsonObj = (JsonArray ) jObj.parse(json);
for(int i = 0; i < jsonObj.size(); i++) {
JsonObject jsonObject = jsonObj.get(i).getAsJsonObject();
System.out.println(jsonObject.get("name").getAsString());
}
return json;
}
}
Please help me to solve this
#RequestMapping(value = "/apis/test", method = RequestMethod.GET, produces = "application/json")
The above means this is a HTTP GET method which does not normally accept data. You should be using a HTTP POST method eg:
#RequestMapping(value = "/apis/test", method = RequestMethod.POST, consumes = "application/json")
public #ResponseBody String test(#RequestParam final String param1, #RequestParam final String param2, #RequestBody final String body) throws JsonIOException {
then you can execute POST /apis/test?param1=one&param2=two and adding strings in the RequestBody of the request
I hope this helps!

Resources