Spring web server considers part of uri as path variable during testing - java

There are two kind of similar endpoints, let's assume:
POST devices/{uuid}/{imei} and POST devices/{uuid}/device-info. The first one is to update IMEI (delivered via path variable) of device specified by UUID and the second one is to update its other parameters (delivered with request as json body).
While server is working "normally" from a jar file, both endpoints works properly how it is described above, which was tested by Postman. But when I run integration tests (with maven or directly through IntelliJ), sending POST request to devices/{uuid}/device-info is interpret on server side as a request to devices/{uuid}/{imei}, where phrase "device-info" is treated as IMEI number.
For integration tests I use autoconfigured MockMvc class and SpringBootTest + Mockito + JUnit4 utilities. webEnvironment is set as SpringBootTest.WebEnvironment.MOCK and everything is ran with SpringRunner.
I was looking for solutions, but actually found nothing. Has anyone met with something similar?
EDIT:
I'm adding API declarations if it can help.
#ResponseStatus(value = HttpStatus.NO_CONTENT, reason = "Device info successfully updated")
#PutMapping(value = "/devices/{deviceUuid}/device-info", consumes = {"application/json"})
ResponseEntity<Void> updateDeviceInfo(#Valid #RequestBody DeviceInfo deviceInfo);
#ResponseStatus(value = HttpStatus.NO_CONTENT, reason = "Device IMEI successfully updated")
#PutMapping(value = "/devices/{deviceUuid}/{imei}")
ResponseEntity<Void> updateDeviceImei(#PathVariable("deviceUuid") UUID deviceUuid, #PathVariable("imei") String imei);
The test itself is as simple as it can be:
DeviceInfo deviceInfo = this.prepareDeviceInfo();
String url = String.format("/v3/devices/%s/device-info", super.firstDeviceUuid);
mvc.perform(put(url)
.content(asJsonString(deviceInfo)))
.andExpect(status().is(204));
where asJsonString is simple helper method to prepare JSON from an object with Jackson methods.

Not sure what is the problem in your case. But I tried this code and it works for me
#RestController
#Slf4j
public class DeviceController {
#ResponseStatus(value = HttpStatus.NO_CONTENT, reason = "Device info successfully updated")
#PutMapping(value = "/devices/{deviceUuid}/device-info", consumes = {"application/json"})
ResponseEntity<Void> updateDeviceInfo(#RequestBody Product product, #PathVariable("deviceUuid") UUID deviceUuid){
log.info("Inside updateDeviceInfo");
return ResponseEntity.ok().build();
};
#ResponseStatus(value = HttpStatus.NO_CONTENT, reason = "Device IMEI successfully updated")
#PutMapping(value = "/devices/{deviceUuid}/{imei}")
ResponseEntity<Void> updateDeviceImei(#PathVariable("deviceUuid") UUID deviceUuid, #PathVariable("imei") String imei){
log.info("Inside updateDeviceInfo");
return ResponseEntity.ok().build();
};
}
For test cases
#SpringBootTest
#AutoConfigureMockMvc
public class DeviceControllerTest {
#Autowired
private MockMvc mvc;
#Autowired
private ObjectMapper objectMapper;
#Test
public void test() throws Exception {
Product product = new Product();
String url = String.format("/devices/%s/device-info", UUID.randomUUID().toString());
mvc.perform(put(url)
.content(objectMapper.writeValueAsString(product)))
.andExpect(status().is(204));
}
#Test
public void test2() throws Exception {
Product product = new Product();
String url = String.format("/devices/%s/%s", UUID.randomUUID().toString(),UUID.randomUUID().toString());
mvc.perform(put(url))
.andExpect(status().is(204));
}
}

I've finally found an answer. When I just commented devices/{uuid}/{imei} endpoint handler in controller, test's result status was 415, so it looked like no handler was found in controller. Then I found this solution: Spring MVC testing results in 415 error which worked for me perfectly.
I just set in my test case a content type to MediaType.APPLICATION_JSON_UTF8 as below and thanks to that it was correctly interpret on the server side.
mvc.perform(put(url)
.content(mapper.writeValueAsString(deviceInfo))
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(status().is(204));
EDIT: MediaType.APPLICATION_JSON works well too.

Related

SpringBoot + Postman #RequestMapping value = "getImage/{imageName:.+}"

I an creating an endpoint with spring boot...i can upload image to folder and save it via postman everythink works good.
i have a problem with get method when i am adding the value #RequestMapping value = "getImage/{imageName:.+}" in postman i add http://localhost:8080/api/images/getImage/{burger+png}
is that corect ???
#RequestMapping(value = "api/images")
public class ImageController {
#Autowired
public ImageService imageService;
#PostMapping(value ="upload")
public ResponseEntity uploadImage(#RequestParam MultipartFile file){
return this.imageService.uploadToLocalFileSystem(file);
}
#GetMapping(
value = "getImage/{imageName:.+}",
produces = {MediaType.IMAGE_JPEG_VALUE,MediaType.IMAGE_GIF_VALUE,MediaType.IMAGE_PNG_VALUE}
)
public #ResponseBody byte[] getImageWithMediaType(#PathVariable(name = "imageName") String fileName) throws IOException {
return this.imageService.getImageWithMediaType(fileName);
}
}
what should be the correct request url ???
It seems like it's reaching the backend fine, but failing to find path. Usually API endpoints end with parameters with a slug or query param. You can try either of the following to see if it works:
http://localhost:8080/api/images/getImage/burger.png
http://localhost:8080/api/images/getImage?imageName=burger.png
Keep in mind, you want to make sure that file exists at the path it's mentioning at the very top of the trace in the JSON response. This may depend on how you uploaded the file and with what name.

How to write mockito test for post request

This function is used to update the user details in the database. can someone help me to write test cases for this function.
#RequestMapping(value = "/updateDetails", method = RequestMethod.POST)
public String updateVendorDetails(#Valid #ModelAttribute("users") Users users, BindingResult result,Model model) {
logger.info("{}.{}",new VendorController().getClass().getPackageName(), new VendorController().getClass().getName());
if(result.hasErrors()) {
model.addAttribute("edit","edit");
logger.warn("Function: updateVendorDetails(), Information: Error while updating vendor details");
return register.toString();
}
userDao.updateVendorDetails(users);
logger.info("Function: updateVendorDetails(), Information: Vendor details updated successfully");
return vendor.toString();
}
Update
Code:
mockMvc.perform(post("/updateDetails").accept(MediaType.TEXT_HTML).params(params)).andExpect(status().isOk());
Resulting error:
This says that post method is forbidden and my test fails
This is my Test class
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class TestVendorPage {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();
}
#WithMockUser(roles = "VENDOR")
#Test
public void testIfUpdateEdtailsIsAvailableOnlyForVendor() throws Exception {
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("firstName", "vinod");
params.add("lastName", "babu");
params.add("contactNumber", "9952016709");
mockMvc.perform(post("/updateDetails").accept(MediaType.TEXT_HTML).params(params)).andExpect(status().isOk());
}
}
Regarding your update:
Thank you for clarifying your post with a specific error/specific problem.
For that specific error - HTTP 403: Forbidden - this should resolve the problem:
Unit test Springboot MockMvc returns 403 Forbidden
i think probleam is happend in "mockMvc" object is not
autowired.mockMvc object should load from WebApplicationContext in
before program run.
Please - PLEASE - consider looking at one or more of the links I cited above.
baeldung.com: Testing in Spring Boot
spring.io: Testing the Web Layer
mkyong.com: Spring REST Integration Example
I've found all three sites very valuable resources. Time spent with these tutorials will help you a great deal!

org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation in java spring boot

I am developing chat application using java springboot and Angular 7. I am using events in spring boot and angular. I am trying to generate events in spring boot for angular to listen the event. However, I am getting following error:
Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation]
Here is my controller code in springboot:
#CrossOrigin("*")
#RestController
#RequestMapping("/chat")
public class MessageController {
#Autowired
MessageService messageService;
#Autowired
private ApplicationEventPublisher applicationEventPublisher;
private static final Logger logger = LoggerFactory.getLogger(MessageController.class);
#PostMapping(consumes = "application/json", produces = "application/json")
public GenericApiResponse<Map<String, Object>>message(#RequestBody MessageRequest req) {
logger.info("MessageController:: messagemethod [POST] /chat");
GenericApiResponse<Map<String, Object>> responseObj = new GenericApiResponse<>();
Object returnValue = new Object();
try {
returnValue = messageService.translateText(req);
} catch (Exception e) {
e.printStackTrace();
logger.error("EXCEPTION: "+e.getStackTrace().toString());
responseObj.setStatus(Constants.ERROR);
responseObj.setMessage("Internal Server Error");
}
Map<String, Object> resMap = new HashMap<>();
resMap.put("result", returnValue);
resMap.put("sender", req.getSender());
responseObj.setResponseObject(resMap);
responseObj.setStatus(Constants.SUCCESS);
MessageEvent messageEvent = new MessageEvent(this,"eventName", responseObj);
applicationEventPublisher.publishEvent(messageEvent);
return responseObj;
}
I am unable to figure out what is the issue and how to solve it. Please help me to solve this issue.
Thanks in advance :)
From first look at your code, I can observe following problems:
#ResponseBody is added but no response is returned i.e. method type is void.
produces = "application/json" doesn't make sense for a void method returning no response.
Hence, for a rest endpoint always return some response. You can fix it by putting following as return statement in the end in your method:
return ResponseEntity.ok("your message");
Also, #ResponseBody means that response is always serialized to json hence, no need to specify , produces = "application/json" explicitly.
Update:
Can you please also try replacing consumes = "application/json", produces = "application/json" with
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
And
ensure that request headers are set to application/json.
Also, ensrue jackson dependencies are in place.
Solution: EventSource in angular takes Content-Type : text/event-stream by default. So I created new method and added #RequestHeader(value = "Content-Type", defaultValue = "text/event-stream") as parameter.
Automatic conversion of Objects are neglected if you don't have getter method for the return type object. If you don't have getter method for GenericApiResponse object add one.

JerseyTest framework path encoding replaces ? with %3F

I have a failing test, which should be passing. The service works fine, but the JerseyTest JUnit test is failing with status 400.
Using Postman or a browser, when I try this URL against the deployed service:
http://localhost:8080/myService/123?appId=local&userId=jcn
I get correct result, status 200 and see the following in the log:
INFO: 4 * Server has received a request on thread http-nio-8080-exec-5
4 > GET http://localhost:8080/myService/123?appId=local&userId=jcn
Note the ? in the URL, which is correct.
But when I try this unit test in my JeryseyTest-extended Junit class:
#Test
public void getWithCorrectUrlExecutesWithoutError()
{
String x = target("myService/123?appId=local&userId=jcn").request().get(String.class);
}
it fails with a status 400, and I see this in the log:
INFO: 1 * Server has received a request on thread grizzly-http-server-0
1 > GET http://localhost:9998/myService/123%3FappId=local&userId=jcn
Note that the ? has been replaced with %3F.
I don't understand what is happening. If I try the "%3F" URL in the browser, I see the same 400 error from the unit test. So I feel somewhat certain that the encoding of the url is the problem.
Here is my Jersey resource, partial listing because it's kind of long, but I am pretty sure this is the relevant part:
#Component
#Path("/myService")
public class MyResource
{
#Autowired
SomeDao someDao;
#NotBlank
#QueryParam("appId")
private String appId;
#NotBlank
#QueryParam("userId")
private String userId;
#GET
#Path("/{id}")
#Produces(MediaType.APPLICATION_JSON)
public Status getStatus(#NotBlank #PathParam("id") String id)
{
errors = new ArrayList<>();
Status retVal;
if(validateId(id))
{
retVal = someDao.getStatus(id);
}
else
{
throw new BadParameterException(String.join(" | ", errors));
}
return retVal;
}
}
You can use the queryParam method on your WebTarget instance:
String x = target("myService/123")
.queryParam("appId", "local")
.queryParam("userId", "jcn")
.request()
.get(String.class);

spring MockMvc testing for model attribute

I have a controller method for which i have to write a junit test
#RequestMapping(value = "/new", method = RequestMethod.GET)
public ModelAndView getNewView(Model model) {
EmployeeForm form = new EmployeeForm()
Client client = (Client) model.asMap().get("currentClient");
form.setClientId(client.getId());
model.addAttribute("employeeForm", form);
return new ModelAndView(CREATE_VIEW, model.asMap());
}
Junit test using spring mockMVC
#Test
public void getNewView() throws Exception {
this.mockMvc.perform(get("/new")).andExpect(status().isOk()).andExpect(model().attributeExists("employeeForm")
.andExpect(view().name("/new"));
}
I am getting NullPointerException as model.asMap().get("currentClient"); is returning null when the test is run, how do i set that value using spring mockmvc framework
As an easy work around you should use MockHttpServletRequestBuilder.flashAttr() in your test:
#Test
public void getNewView() throws Exception {
Client client = new Client(); // or use a mock
this.mockMvc.perform(get("/new").flashAttr("currentClient", client))
.andExpect(status().isOk())
.andExpect(model().attributeExists("employeeForm"))
.andExpect(view().name("/new"));
}
The response is given as string chain (I guess json format, as it is the usual rest service response), and thus you can access the response string via the resulting response in this way:
ResultActions result = mockMvc.perform(get("/new"));
MvcResult mvcResult = result.andExpect(status().isOk()).andReturn();
String jsonResponse = mvcResult.getResponse().getContentAsString();
And then you can access to the response via getResponse().getContentAsString(). If json/xml, parse it as an object again and check the results. The following code simply ensures the json contains string chain "employeeForm" (using asertJ - which I recommend)
assertThat(mvcResult.getResponse().getContentAsString()).contains("employeeForm")
Hope it helps...

Categories

Resources