How to pass a List<Object> in Rest Template? - spring

I need to write a client code to call a method in a webservice and this is my client code.
#HystrixCommand
public List<Location> saveLocation (List<LocationDTO> locationDTO) {
HttpEntity<List<LocationDTO>> httpEntity = new HttpEntity<>(locationDTO);
ResponseEntity<List<Location>> response =
restTemplate.exchange(locationProperties.getBaseURL(),HttpMethod.POST,
httpEntity,new ParameterizedTypeReference<List<Location>>() {
});
return getResponseBody(response);
}
For which I write Junit to test this piece of code.
#Test
public void saveLocationTest() throws Exception {
List<LocationDTO> locationList = new ArrayList<>();
LocationDTO locationDTO = new LocationDTO();
locationList.add(locationDTO);
Location location = new Location();
location.setLocationID(100);
mockServer.expect(requestTo(baseURL)).andExpect(method(HttpMethod.POST))
.andRespond(withSuccess("{\"locationID\":100}",MediaType.APPLICATION_JSON));
List<Location> response = locationClient.saveLocation(locationList);
assertEquals(response, location);
}
But I get the following error when I run the Junit, which also means my client code is wrong.
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not >deserialize instance of java.util.ArrayList out of START_OBJECT token
at [Source: java.io.ByteArrayInputStream#175b9425; line: 1, column: 1]
Rest Template and junit works fine if I just pass LocationDTO instead of List<LocationDTO>.Could anyone please help me with this?

Related

spring tests with mocks and HttpMessageConversionException

i have something like that in my ParkingServiceController.
#PostMapping("/departure")
public ResponseEntity<String> departure(#RequestBody CarAtGateModel carAtGateModel) throws UnidentifiedCarException {
CarAndParkingEntity carAndParkingEntity = carsAndParkingsRepository.findByIdCar(
carAtGateModel.getCarEntity().getIdCar()).orElseThrow(() -> new UnidentifiedCarException());
carAndParkingEntity.setIdParking("-1");
carsAndParkingsRepository.flush();
return new ResponseEntity<>(responsesMessages.gateUp(), HttpStatus.OK);
}
and next i wanted to do test with some mocks.
#Test
public void testArrivalWhenParkingIdNotExists() {
//given
CarAndParkingEntity carAndParkingEntity = mock(CarAndParkingEntity.class);
carAtGateModel = mock(CarAtGateModel.class);
//when
when(carsAndParkingsRepository.findByIdCar(anyString())).thenReturn(Optional.of(carAndParkingEntity));
HttpEntity<CarAtGateModel> request = new HttpEntity<>(carAtGateModel);
ResponseEntity response = testRestTemplate.postForEntity("/departure", request, String.class);
//then
assertEquals("Parking with that id does not exists", response.getBody());
}
but i'm getting that exception every time with every code change in test
org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.mockito.internal.creation.bytebuddy.ByteBuddyCrossClassLoaderSerializationSupport]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.mockito.internal.creation.bytebuddy.ByteBuddyCrossClassLoaderSerializationSupport and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.example.parkingservice.models.CarAtGateModel$MockitoMock$1316802841["mockitoInterceptor"]->org.mockito.internal.creation.bytebuddy.MockMethodInterceptor["serializationSupport"])
When i'm not using any mock test passess correctly so imo i'm doing something wrong with mocking
At first sight, as your ParkingServiceController has a composition with CarsAndParkingRepository, you should mock that dependency first. I don't know why you are mocking carAtGateModel, you can use a real object representing the the data you want to pass to the controller (the same applies to carAndParkingEntity).
It would be helpful if you add more details, explaining what you want to test because it's not totally clear the assertion you are doing.

MockMvc Test does not get to the endpoint for a Multipart file in a RestController

I am calling a service in an orders controller which receives a multipart file and processes it and saving it into a database. I am trying to create a Spring Rest Doc for it but it is not even hitting the endpoint. I am creating a list of orders which is what the service expects. It receives the order as a stream as shown and converts into a stream of orders before saving it into a database. I have shown the main part of the controller and my code for generating the rest docs. When I run the code I get the following exception, it never even hits the endpoint when I set a breakpoint. I also used fileupload() but that did not work either.
Exception is:
Content type = application/json
Body = {"path":"/orders/order_reception","exceptionName":
"MissingServletRequestPartException","message":"Required request part 'uploadFile' is not
present",
"rootExceptionName":"MissingServletRequestPartException",
"rootMessage":"MissingServletRequestPartException: Required request part 'uploadFile' is not present"}
#RestController
#RequestMapping(value = "/orders")
#Validated
class OrderController{
#PostMapping(path = "/order_reception")
public ResponseEntity receiveData(#RequestPart MultipartFile uploadFile,
HttpServletRequest request,
HttpServletResponse response) {
if (!uploadFile.isEmpty()) {
try {
Reader reader = new InputStreamReader(request.getInputStream()));
... save file
return new ResponseEntity<>(HttpStatus.HttpStatus.CREATED);
} catch (Exception e) {
return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
#Test
public void sendData() throws Exception {
ObjectMapper mapper = new ObjectMapper();
Order order = repository.getOrder("1233333");
List<Order> orderList = new ArrayList<>():
resourceList.add(order);
MockMultipartFile orderFile = new MockMultipartFile("order-data", "order.json", "application/json",
mapper.writeValueAsString(orderList).getBytes(Charset.defaultCharset()));
mockMvc.perform(multipart("/orders/order_reception")
.file(orderFile))
.andExpect(status().isCreated())
.andDo(document("send-order",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())));
}
Thank you Marten Deinum, your suggestion that the file name was wrong fixed it.
I simply changed name in the MockMultipartFile( "uploadsFile", ...)

spring boot unit testing using testng

How to write a POST method test case if the return type of a particular create method in the service layer is ResponseEntity<Object>?
This is my createOffer method:
public ResponseEntity<Object> createOffer(Offer offer) {
Offer uoffer = offerRepository.save(offer);
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{jobTitle}").
buildAndExpand(uoffer.getJobTitle()).toUri();
return ResponseEntity.created(location).build();
}
and this is its corresponding test class method:
#Test
public void testCreateOffer() {
Offer offer = new Offer("SE",new Date(),5);
Mockito.when( offerRepository.save(offer)).thenReturn( offer);
assertThat(offerServiceImpl.createOffer(offer)).isEqualTo(offer);
}
Here I am getting an error while running this test case which is no current servlet request attributes and exception is:
java.lang.IllegalStateException
Why is it coming
This answers the above question.
Hope it helps when someone finds the same issue !!!
#Test
public void testCreateOffer() {
Offer offer = new Offer("SE",new Date(),5);
MockHttpServletRequest request = new MockHttpServletRequest();
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{jobTitle}").
buildAndExpand(offer.getJobTitle()).toUri();
ResponseEntity<Object> response = ResponseEntity.created(location).build();
Mockito.when( offerRepository.save(offer)).thenReturn(offer);
assertThat( offerServiceImpl.createOffer(offer)).isEqualTo(response);
}
Problem is that in your method you want to get infromation from class ServletUriComponentsBuilder. When you open this class in comment is
UriComponentsBuilder with additional static factory methods to create
links based on the current HttpServletRequest.
So it means when your application is running on server (e.g. tomcat) you have context and you can read information from HttpServletRequest. But in junit you don't have context and you can't get this iformation. So when your code is runnig and reach the ServletUriComponentsBuilder.fromCurrentRequest() then the code is done. So you have to mock it. Look at this link it can help you.
ServletUriComponentsBuilderTests
Kotlin.
I was getting java.lang.IllegalStateException: No current ServletRequestAttributes cause I had this line in my service:
val location = ServletUriComponentsBuilder.fromCurrentRequest().build().toUri()
I have put the following into my setUp() function:
#BeforeEach
fun setup() {
MockitoAnnotations.openMocks(this)
val request = MockHttpServletRequest()
RequestContextHolder.setRequestAttributes(ServletRequestAttributes(request))
}

Why this externa web service call go into error only when the call is performed using Spring RestTemplate?

I am working on a Spring project implementing a simple console application that have to call an external REST web service passing to it a parameter and obtaining a response from it.
The call to this webservice is:
http://5.249.148.180:8280/GLIS_Registration/6
where 6 is the specified ID. If you open this address in the browser (or by cURL tool) you will obtain the expected error message:
<?xml version="1.0" encoding="UTF-8"?>
<response>
<sampleid>IRGC 100000</sampleid>
<genus>Oryza</genus>
<error>PGRFA sampleid [IRGC 100000], genus [Oryza] already registered for this owner</error>
</response>
This error message is the expected response for this request and I correctly obtain it also using cURL tool to perform the request.
So I have to perform this GET request from my Spring application.
To do it I create this getResponse() method into a RestClient class:
#Service
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RestClient {
RestTemplate restTemplate;
String uriResourceRegistrationApi;
public RestClient() {
super();
restTemplate = new RestTemplate();
uriResourceRegistrationApi = "http://5.249.148.180:8280/GLIS_Registration/7";
}
public ResponseEntity<String> getResponse() {
ResponseEntity<String> response = restTemplate.getForEntity(uriResourceRegistrationApi, String.class);
return response;
}
}
Then I call this method from this test method:
#Test
public void singleResourceRestTest() {
System.out.println("singleResourceRestTest() START");
ResponseEntity<String> result = restClient.getResponse();
System.out.println("singleResourceRestTest() END");
}
But I am experiencing a very strange behavior, what it happens is:
1)The call to my external web service seems that happens (I saw it from the web services log).
2) The web service retrieve the parameter having value 7 but then it seems that can't use it as done without problem performing the request from the browser or by the shell statment:
curl -v http://5.249.148.180:8280/GLIS_Registration/7
But now, calling in this way, my webservice (I can't post the code because it is a WSO2 ESB flow) give me this error message:
<200 OK,<?xml version="1.0" encoding="UTF-8"?>
<response>
<error>Location information not correct</error>
<error>At least one between <genus> and <cropname> is required</error>
<error>Sample ID is required</error>
<error>Date is required</error>
<error>Creation method is required</error>
</response>,{Vary=[Accept-Encoding], Content-Type=[text/html; charset=UTF-8], Date=[Fri, 05 May 2017 14:07:09 GMT], Transfer-Encoding=[chunked], Connection=[keep-alive]}>
Looking the web service log it seems that performing the call using RestTemplate it have some problem to use the retrieved ID=7 to perform a database query.
I know it looks terribly strange and you can see: "The problem is of your web service and not of the Spring RestTemplate". This is only partially true because I implemented this custom method that perform a low level Http GET call, this callWsOldStyle() (putted into the previous RestClient class):
public void callWsOldStyle() {
HttpURLConnection connection = null;
BufferedReader reader = null;
try {
URL restAPIUrl = new URL("http://5.249.148.180:8280/GLIS_Registration/7");
connection = (HttpURLConnection) restAPIUrl.openConnection();
connection.setRequestMethod("GET");
// Read the response
reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
StringBuilder jsonData = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
jsonData.append(line);
}
System.out.println(jsonData.toString());
}catch(Exception e) {
e.printStackTrace();
}
finally {
// Clean up
IOUtils.closeQuietly(reader);
if(connection != null)
connection.disconnect();
}
}
Using this method instead the RestTemplate one it works fine and this line:
System.out.println(jsonData.toString());
print the expected result:
<?xml version="1.0" encoding="UTF-8"?><response><sampleid>IRGC 100005</sampleid><genus>Oryza</genus><error>PGRFA sampleid [IRGC 100005], genus [Oryza] already registered for this owner</error></response>
To summarize:
Calling my WS from the browser it works.
Calling my WS using cURL it works.
Calling my WS using my callWsOldStyle() method it works.
Calling my WS using the method that use RestTemplate it go into error when my WS receive and try to handle the request.
So, what can be the cause of this issue? What am I missing? Maybe can depend by some wrong header or something like this?
As Pete said you are receiving an internal server error (status code 500) so you should check the server side of this rest service.
In any case you can do the following for the resttemplate
create an org.springframework.web.client.RequestCallback object if
you need to do something in the request
create an org.springframework.web.client.ResponseExtractor<String>
object in order to extract your data
use the resttemplate
org.springframework.web.client.RequestCallback
public class SampleRequestCallBack implements RequestCallback
{
#Override
public void doWithRequest(ClientHttpRequest request) throws IOException
{
}
}
org.springframework.web.client.ResponseExtractor
public class CustomResponseExtractor implements ResponseExtractor<String>
{
private static final Logger logger = LoggerFactory.getLogger(CustomResponseExtractor.class.getName());
#Override
public String extractData(ClientHttpResponse response) throws IOException
{
try
{
String result = org.apache.commons.io.IOUtils.toString(response.getBody(), Charset.forName("UTF8"));
if( logger.isInfoEnabled() )
{
logger.info("Response received.\nStatus code: {}\n Result: {}",response.getStatusCode().value(), result);
}
return result;
}
catch (Exception e)
{
throw new IOException(e);
}
}
}
REST TEMPLATE CALL
#Test
public void testStack()
{
try
{
String url = "http://5.249.148.180:8280/GLIS_Registration/6";
String response = restTemplate.execute(url, HttpMethod.GET, new SampleRequestCallBack(), new CustomResponseExtractor());;
logger.info(response);
}
catch (Exception e)
{
logger.error("Errore", e);
}
}
Angelo

Custom json response for internal exception in spring

While implementing a global exception handler in Spring, I noticed that in case of a not recognized Accept header, Spring would throw it's own internal error. What I need is to return a custom JSON error structure instead. Works fine for application specific exceptions and totally fails for Spring HttpMediaTypeNotAcceptableException.
This code tells me "Failed to invoke #ExceptionHandler method: public java.util.Map RestExceptionHandler.springMalformedAcceptHeaderException()" when I try to request a page with incorrect Accept header. Any other way to return custom JSON for spring internal exceptions?
#ControllerAdvice
public class RestExceptionHandler {
#ExceptionHandler(value = HttpMediaTypeNotAcceptableException.class)
#ResponseBody
public Map<String, String> springMalformedAcceptHeaderException() {
Map<String, String> test = new HashMap<String, String>();
test.put("test", "test");
return test;
}
}
Eventually figured that the only way is to do the json mapping manually.
#ExceptionHandler(value = HttpMediaTypeNotAcceptableException.class)
#ResponseBody
public String springMalformedAcceptHeaderException(HttpServletResponse response) {
// populate errorObj, set response headers, etc
ObjectWriter jsonWriter = new ObjectMapper().writer();
try {
return jsonWriter.writeValueAsString(errorObj);
} catch(Exception e){}
return "Whatever";
}

Resources