How to make unitest simulation with mockServer, for external local server - java

I need to test this method.
public List<HotelInfo> getQueryHotelInfoViaURL(int id) {
private RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8081/hotels?id=" + id;
try {
HotelInfo[] hotelInfoArray = restTemplate.getForObject(new URI(url), HotelInfo[].class);
return Arrays.asList(hotelInfoArray);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
My test is not working:
mvc.perform(get("/offers?arrival=LWO&persons=3&los=2")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().is(200));
I need to simulate fetching url:
http://localhost:8081/hotels?id=
with response Object:
[{"id":1,"hotelName":"SunshineHotel","slogan":"Be where the good life is","hotelImageURL":"https://cdn-cyclingtips.pressidium.com/wp-content/uploads/2018/02/cycling-Switzerland-Andermatt-14.jpg"}]
How i can do this?

Related

Async call of a FeignClient Springboot with CompletableFuture

I want to call async rest endpoints with Feign client and have done the below changes.
When calling it the CompletableFuture.get() doesn't complete.
The while keeps looping...
while(!combinedFuture.isDone()) { log.info("useraccount - waiting for combinedFuture 2: " + request.toString()); }
Interface to call the API:
#FeignClient(value = "clientUser", url = "http://localhost:8898/springboot", fallback = UserFallback.class)
public interface User {
#RequestMapping(method = RequestMethod.GET, value = "/user/", produces = "application/json")
#Async
CompletableFuture<UserInfo> findUserInfo(#RequestHeader(value = "Authorization", required = true) String authorizationHeader);
}
Controller method:
#PostMapping(value = "/springboot/useraccount/", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> userAccount(#requestbody #Valid AuthRequest request) {
log.info("useraccount - request received with request body: " + request.toString());
try {
if (Strings.isBlank(request.getUsername()) || Strings.isBlank(request.getPassword())) {
throw new BadRequestException("invalid username or password");
}
String token = authorisationService.obtainAuthToken(request.getUsername(), request.getPassword());
CompletableFuture<UserInfo> userInfo = clientUser.findUserInfo(token);
CompletableFuture<UserAccountInfo> userAccountInfo = clientAccount.findAccountInfo(token);
CompletableFuture<Void> combinedFuture
= CompletableFuture.allOf(userInfo, userAccountInfo);
while(!combinedFuture.isDone()) {
log.info("useraccount - waiting for combinedFuture 2: " + request.toString());
}
Optional<UserAccountResponse> userAccountResponse = userAccountService.getAccountInfo(
userAccountInfo.get(), userInfo.get()
);
if (userAccountResponse.isEmpty()) {
throw new BadCredentialsException("Bad Credentials");
}
return ResponseEntity.ok().body(userAccountResponse);
} catch (BadCredentialsException | UnAuthorizedException ex) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
} catch (BadRequestException ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
} catch (ExecutionException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
} catch (InterruptedException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
(update) Have changed a bit to use CompletableFuture.supplyAsync
but now the object is always null...
#Service
public class AccountService {
#Autowired
Account accountClient;
#Async
public Optional<UserAccountInfo> getAccountInfo(String token) {
return Optional.of(accountClient.findAccountInfo(token));
}
}
Managed to solve it like this:
#Async
public CompletableFuture<UserAccountInfo> getAccountInfo(String token) {
try {
System.out.println(
"Current Thread account Name: "
+ Thread.currentThread().getName());
Thread.currentThread().sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return CompletableFuture.completedFuture(accountClient.findAccountInfo(token).getBody());
}
The same for the userInfo service. Then on the controller:
CompletableFuture<UserAccountInfo> userAccountInfo = accountService.getAccountInfo(token);
CompletableFuture<UserInfo> userInfo = userService.getUserInfo(token);
Optional<UserAccountResponse> userAccountResponse = userAccountService.getAccountInfo(
userAccountInfo.get(),userInfo.get()
);
So that means both services will start running each in a new thread while the main thread continues ran until find the first .get().
By doing it like that the maximum waiting time to finish is the time of the thread that takes more time and not the sum of both as it happens if it is a synchronous.
Thanks!

Consuming external API and converting from JSON string to bean doesn't initialize properties. NULL object attributes. Spring Boot, Maven

This is the response I get from the API.
{"get":"statistics","parameters":{"country":"romania"},"errors":[],"results":1,"response":[{"continent":"Europe","country":"Romania","population":19016885,"cases":{"new":"+4521","active":156487,"critical":431,"recovered":2606660,"1M_pop":"148707","total":2827936},"deaths":{"new":"+35","1M_pop":"3407","total":64789},"tests":{"1M_pop":"1149381","total":21857638},"day":"2022-03-24","time":"2022-03-24T07:30:04+00:00"}]}
#RestController
public class CovidTrackerRestController {
#GetMapping("/hello")
public String showCovidInformation() {
// connect to a covid database
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://covid-193.p.rapidapi.com/statistics?country=romania"))
.header("X-RapidAPI-Host", "covid-193.p.rapidapi.com")
.header("X-RapidAPI-Key", "mykey")
.method("GET", HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse<String> response = null;
try {
response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
} catch (IOException | InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// get the information
String responseString = response.body();
System.out.println(responseString);
Response romaniaData = null;
try {
romaniaData = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.readValue(responseString, Response.class);
} catch (JsonProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// format the information
System.out.println(romaniaData);
// send the information to html page
return "/tracker";
}
}
And this is my Bean class which is annotated with #Bean in the configurator class alonside the RestTemplate bean. Other properties such as Cases, Deaths etc are configured same as Response class except being declared as #Bean in the configurator because from what I know once I declare a class #Bean then other references contained automatically become beans as well.
#JsonIgnoreProperties(ignoreUnknown = true)
public class Response {
#JsonProperty("country")
private String country;
#JsonProperty("cases")
private Cases cases;
#JsonProperty("deaths")
private Deaths deaths;
#JsonProperty("day")
private String day;
#JsonProperty("time")
private String time;
#JsonProperty("test")
private Tests tests;
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
Your java class needs to be exact representation of received json. Let's call it Wrapper:
public class Wrapper {
#JsonProperty("response")
private List<Response> responses;
public List<Response> getResponses() {
return this.responses;
}
public void setResponses(List<Response> responses) {
this.responses = responses;
}
#Override
public String toString() {
return "Wrapper{" +
"responses=" + responses +
'}';
}
}
I am omiting some properties - get, results, etc. It looks you don't need them. Then deserialization will look like this:
Wrapper data = null;
try {
data = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.readValue("json", Wrapper.class);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(data);
Few notes:
If json property name matches field name in class, there is no need for #JsonProperty
For tests field annotation should be - #JsonProperty("tests"). Property is tests, not test
If you really want to throw the rest of the data, and only need response property, then you need to write custom deserializer and work the json tree. You can see how to do it in my answer here, or this guide, for example. Like this you can parse the response json to your class, even if their structures do not match.
Yes, your class should be like this:
public class ResponseWrapper {
public List<Response> response;
public setResponse(List<Response> response) {
this.response= response;
}
public List<Response> getResponse() {
return response;
}
}
And class Response is your class as you published it. Your class have to have the same structure as JSON

Unable to cover catch block code coverage

I am not able to cover below HttpClientErrorException catch block in Junit code coverage. How can I do this?
code
#Autowired
private ExceptionHandlerService errorHandler;
public CartResponse callUpdateCart(AddToCartRequest request) {
String url = Utils.formatHttpUrl(cartbaseUrl, CART_UPDATE_CART);
try {
HttpEntity<?> entity = new HttpEntity<>(request);
JsonNode jsonNode = restTemplate.postForObject(url, entity, JsonNode.class);
if (jsonNode.has(Constants.CONTENT) && !jsonNode.path(Constants.CONTENT).path(Constants.PAYLOAD).isMissingNode()) {
jsonNode = jsonNode.path(Constants.PAYLOAD).get(Constants.PAYLOAD);
} else {
errorHandler.error(ErrorMessages.EPO_VALIDATEQ_ERROR_08, jsonNode);
}
return JsonService.getObjectFromJson(jsonNode, CartResponse.class);
} catch (HttpClientErrorException e) {
errorHandler.error(ErrorMessages.EPO_VALIDATEQ_ERROR_08, e.getResponseBodyAsString());
return null;
} catch (HttpServerErrorException e) {
throw new ServiceException(ErrorMessages.EPO_SYSTEM_ERROR, e.getMessage(), url);
}
}
ExceptionHandlerService
#Override
public void error(ResolvableErrorEnum error, String responseBody) {
JsonNode response = JsonService.getObjectFromJson(responseBody, JsonNode.class);
if (null != response && null != response.get(Constants.ERROR)) {
ServiceError serviceError = JsonService.getObjectFromJsonNode(response.get(Constants.ERROR), ServiceError.class);
error(error, serviceError.getErrorId(), serviceError.getMessage());
}
throw new ServiceException(error);
}
junit
#Test(expected = ServiceException.class)
public void test_callUpdateCart_Exception() throws IOException {
AddToCartRequest req = createAddToCartRequest();
String responseBodyStr = "{\"error\":{\"errorId\":\"Service-I-1003\",\"message\":\"Error returned from downstream system.\",\"traceCode\":\"CART;400\",\"details\":[{\"code\":\"400\",\"message\":\"400 Bad Request\"},{\"code\":\"DTV_CAT_ERR_002\",\"message\":\"Error in getting response from catalog.\",\"traceCode\":\"CART;400\"}]}}\r\n";
byte[] body = responseBodyStr.getBytes(StandardCharsets.UTF_8);
HttpClientErrorException e = new HttpClientErrorException(HttpStatus.BAD_REQUEST, "BAD REQUEST", body,
StandardCharsets.UTF_8);
when(restTemplate.postForObject(Mockito.anyString(), Mockito.<HttpEntity<?>>any(), Mockito.eq(JsonNode.class)))
.thenThrow(e);
client.callUpdateCart(req);
}
error
It seems like the mockito.when() is not working properly, since it looks like it is not throwing the exception you are asking it to throw. I had similar issues and tinkering with mockito matchers usually fixes them.
You can check this page for a bit more information regarding "expanding" or restricting your argument matchers.
I think the solution bellow will work:
when(restTemplate.postForObject(Mockito.anyString(), Mockito.any(HttpEntity.class), Mockito.any(JsonNode.class)))
.thenThrow(e);

How to fix RestTemplate NullPointerException in spring boot?

#Service
public class RequestSender {
private static final Logger logger = LoggerFactory.getLogger(RequestSender.class);
#Autowired
private RestTemplate restTemplate;
public MbsFtResponseData sendJsonDataToMBS(final MBSTransactionData transactionData) {
String mbsUrl = MBSConfigConstants.mbsUrl;
try {
logger.info("Sending request method Is Initiated");
HttpEntity<MBSTransactionData> httpEntity = new HttpEntity<>(transactionData);
ResponseEntity<MbsFtResponseData> response = restTemplate.exchange(mbsUrl, HttpMethod.POST, httpEntity,
MbsFtResponseData.class);
if (response != null) {
HttpStatus status = response.getStatusCode();
if (status.is2xxSuccessful()) {
logger.info("Response getting back is succeded with the status code {}", status.value());
return response.getBody();
} else {
logger.error("ERROR Response getting back is with the status code {}", status.value());
throw new BBPSMBSException("Error is while connecting to mBS server", status.value());
}
} else {
logger.error("Null value::::::::::::response is null");
}
} catch (Exception e) {
e.printStackTrace();
logger.error("ERROR :::{}:: is occered ", e.getCause());
}
return new MbsFtResponseData("Server Not responding or Busy", 500, "FAILED");
}
}
java.lang.NullPointerException at com.npst.bbps.middleware.mbs.RequestSender.sendJsonDataToMBS(RequestSender.java:26) at com.npst.bbps.middleware.mbs.MbsftServiceImpl.mbsFundTransfer(MbsftServiceImpl.java:27) at com.npst.bbps.middleware.controller.ThirdPartyIntegrationCtrl.initiateRefund(ThirdPartyIntegrationCtrl.java:64) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
RestTemplate bean is not found in the BeanFactory, because you wouldn't configured.
You have to define a bean like below in the configuration file.
#Configuration
public class Config {
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

How do you override Play framework controller in a unit testing context?

I am creating a basic POST JSON api endoint. I would like to unit test it, and want to make sure I am doing it appropriately in the Play framework. So far I am using Guice for dependency injection and JUnit for my unit testing library.
Here is my controller code:
public class NotificationController extends Controller {
private RabbitQueueService _rabbitQueueService;
#Inject
public NotificationController(RabbitQueueService service) {
_rabbitQueueService = service;
}
#BodyParser.Of(BodyParser.Json.class)
public Result post() {
ObjectMapper mapper = new ObjectMapper();
Notification notification;
try {
JsonNode notificationJsonNode = Controller.request().body().asJson();
notification = mapper.readValue(notificationJsonNode.toString(),
Notification.class);
_rabbitQueueService.push(notification);
return Results.created(notificationJsonNode, "UTF-8");
} catch (JsonParseException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return Results.badRequest();
}
}
My RabbitQueueService code:
public class RabbitQueueService {
private Channel _channel;
private Connection _connection;
public RabbitQueueService() {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(Config.RABBITMQ_HOST);
try {
_connection = factory.newConnection();
_channel = _connection.createChannel();
_channel.queueDeclare(Config.RABBITMQ_QUEUE, false, false, false, null);
_channel.exchangeDeclare(Config.RABBITMQ_EXCHANGE, "fanout");
_channel.queueBind(Config.RABBITMQ_QUEUE, Config.RABBITMQ_EXCHANGE, "");
} catch (IOException e) {
e.printStackTrace();
}
}
public void push(Notification notification) {
try {
_channel.basicPublish(Config.RABBITMQ_EXCHANGE, "", null, notification.getBytes());
_channel.close();
_connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void pop() {
}
}
My MockQueueService code:
public class MockQueueService extends RabbitQueueService {
#Override
public void push(Notification notification) {
/* Do nothing because you know... thats what I do */
}
#Override
public void pop() {
/* Do nothing because you know... thats what I do */
}
}
and finally my current unit test code:
public class ApplicationTest {
#Test
public void addMessageToQueue() {
running(fakeApplication(), new Runnable() {
public void run() {
FakeRequest request = new FakeRequest("/POST", "/api/v1/notifications");
Notification notification = new Notification(UUID.randomUUID(),
new NotificationType(UUID.randomUUID(),
"Critical"),
"Test notification message");
try {
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(notification);
JsonNode node;
node = mapper.readTree(json);
request.withJsonBody(node);
} catch (IOException e) {
e.printStackTrace();
}
route(request);
}
});
}
}
This all works fine when making a curl request to test my endpoint through play run. My main question is: how do I use the MockQueueService in my unit test? I don't see anyway to do it with fakeApplication() helper. I could instantiate it directly like
NotificationController nc = new NotificationController(new MockQueueService());
nc.post();
but the problem is I need to override the body of the play request with an appropriate request body and I think I need a FakeRequest for that.
Any help, samples, or advice would be helpful.
UPDATE
I have posted a gist example with the necessary example files. The things specifically that I did to get it working:
Setup a new GlobalUnitTest file that I passed into the fakeApplication helper
Changed NotificationController to be a singleton. This allowed me to pull in the NotificationController instance so I could check the QueueService count as part of the assertion.
FakeApplication takes a bunch of arguments that you could use to inject your new service. You could use a combination of any of these:
additionalPlugins
additionalConfiguration
withGlobal
They each let you specify some additional configuration you could use only during testing. Another thing you could do is have a separate Global object just for testing, that is used to create your controllers. The Global object is used to return your controller instance when you use # in your route definition. Then, you can create a separate application.test.conf that refers to GlobalTest that is loaded when you run play test.

Categories