I have a method that makes a call to external endpoint using io.vertx.ext.web.client.WebClient . I am not able to test the handler method of it.
This is the method that needs to be tested:
public void freshdeskPostRequest(CompletableFuture<ResponseObject> completableFuture, String url, JsonObject jsonObject, String action) {
webClient.postAbs(url)
.putHeader("Content-type", "application/json")
.putHeader(Constants.AUTHORIZATION, freshdeskAuthHandler)
.timeout(fresdeskTimeout)
.sendJsonObject(jsonObject, httpResponseAsyncResult -> {
getFreshdeskResponse(completableFuture, action, httpResponseAsyncResult);
});
}
The method in it getFreshdeskResponse needs to be tested by making a mock call to the url. But the method is called in handler so I am not sure how to mock the call and execute the handler. I checked several answers on the forum as well went through the docs but none of them helped. Please help. I am using Junit, Mockito as testing frameworks. Please help.
You could use ArgumentCaptor of the mockito library to capture the lambda expression and trigger the lambda manually. Ex :
#Captor
ArgumentCaptor<SomeHandlerType> captor; // create ArgumentCaptor for handler
SomeHandlerType is the type of handler. Then call the sendJsonObject() with captor.capture() like so -
mockedRequest.sendJsonObject(captor.capture()); // capture the argument
SomeHandlerType handler = captor.getValue(); // get the handler lambda
handler.handle(dummyResponse); // trigger the handler manually
Related
My server sends a request via WebClient and the code is below:
public String getResponse(String requestBody){
...
WebClient.RequestHeadersSpec<?> request =
client.post().body(BodyInserters.fromValue(requestBody));
String resp =
request.retrieve().bodyToMono(String.class)
.doOnError(
WebClientResponseException.class,
err -> {
// do something
})
.block();
return resp;
}
I wrote a unit test for it and want to mock the WebClient so that I can receive the expected response:
when(webClientMock.post()).thenReturn(requestBodyUriMock);
when(requestBodyUriMock.body(BodyInserters.fromValue(requestBody))).thenReturn(requestHeadersMock);
when(requestHeadersMock.retrieve()).thenReturn(responseMock);
when(responseMock.bodyToMono(String.class)).thenReturn(Mono.just("response"));
String response = someServiceSpy.getResponse(requestBody);
assertEquals(Mono.just("response"), response);
However, the result is not the "response" but a html file. I think I made a mistake somewhere but I don't know how to fix it.
It seems the client referenced in your getResponse method is not set to the mock you have created (webClientMock) in your test.
If you are creating this client object in your getResponse method, I would suggest that you create it using a method that you could mock. Something like
WebClient buildWebClient() {
// build your webclient using the WebClientBuilder
}
You may want to throw a comment and or a #VisibleForTesting annotation on there so it is clear this method exists in order to make testing easier.
Then you can stub this method in your someServiceSpy:
Mockito.doReturn(mockWebClient).when(someServiceSpy).buildWebClient();
This will ensure that your mockWebClient is used in your getResponse method in your test.
Additionally, it seems as though your existing code needs a slight edit.
when(requestBodyUriMock.body(BodyInserters.fromValue(requestBody))).thenReturn(requestHeadersMock);
Should be
when(requestBodyUriMock.body(eq(BodyInserters.fromValue(requestBody)))).thenReturn(requestHeadersMock);
I have figured out the solution that mocks the WebClient directly instead of putting the build logic into a new method to mock it. I wrote my solution here in case someone else needs it future:
Let me put the code example here:
final WebClient client =
WebClient.builder()
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(someValue))
.clientConnector(new ReactorClientHttpConnector(HttpClient.create(someProvider)))
.baseUrl(someUrl)
.defaultHeader(contentType, TEXT_XML_VALUE)
.build();
final WebClient.RequestHeadersSpec<?> request =
client.post().body(BodyInserters.fromValue(reqBody));
First, we must mock the static method builder() of WebClient. If this method is not mocked, mockito can't edit the behavior of this method, and the mocked WebClient would not be used. I found this from the answer to this StackOverflow question; you can read it for more details.: How to mock Spring WebClient and builder
After mocked the builder() with the method provided by the above anwser, you will get a mocked WebClient, it's something like:
when(webClientBuilder.build()).thenReturn(webClientMock);
Then you can start to finish the rest of the work. In my sample code, the client will invoke post() and body(), so write the following:
when(requestBodyUriMock.body(any())).thenReturn(requestHeadersMock);
when(requestHeadersMock.retrieve()).thenReturn(responseMock);
when(responseMock.bodyToMono(String.class)).thenReturn(Mono.just(expectedResponse));
My unit test returned the NPE at the beginning and it because I used
when(requestBodyUriMock.body(BodyInserters.fromValue(requestBody))).thenReturn(requestHeadersMock);
instead of
when(requestBodyUriMock.body(any())).thenReturn(requestHeadersMock);
I think it is because the code not "think" the requestBodyUriMock is using the BodyInserters.fromValue(requestBody for some reasons that I haven't know yet. After I changed it to any(), it worked.
I am writing some contract tests and I am trying to mock my controller in order to test the wanted method. My method should only return status code 200, so not an object, and I do not know how to write this with Mono or Flux and I get an error because of that.
I tried something like this, but it does not work:
Mono<Integer> response = Mono.just(Response.SC_OK);
when(orchestration.paymentReceived(purchase)).thenReturn(response);
How should I write my "when" part in order to verify it returns status code 200?
In order to check response status code you will need to write a more complicated test, using WebTestClient. Like so:
Service service = Mockito.mock(Service.class);
WebTestClient client = WebTestClient.bindToController(new TestController(service)).build();
Now you are able to test:
serialization to JSON or other types
content type
response code
path to your method
invoked method (POST,GET,DELETE, etc)
Unit tests do not cover above topics.
// init mocks
when(service.getPersons(anyInt())).thenReturn(Mono.just(person));
// execute rest resource
client.get() // invoked method
.uri("/persons/1") // requested path
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isOk() // response code
.expectHeader().contentType(MediaType.APPLICATION_JSON)
.expectBody()
.jsonPath("$.firstName").isEqualTo(person.getFirstName())
.jsonPath("$.lastName").isEqualTo(person.getLastName())
// verify you have called your expected methods
verify(service).getPerson(1);
You can find more examples here. Above test is also does not require Spring context, can work with mock services.
I am building a service with RestAPI's. I want to put a custom annotation as mentioned below.
#CustomAnnnotation
public APIResponse apiMethod(APIRequest request) {
}
Functionality of this custom annotation :
Whenever there is a request to this apiMethod, before the execution of this method , i want to call an API in different server with some of the request parameters from this function. Example mentioned below. Basically for every method invocation i want to call a different server.
Instead of doing this
public APIResponse apiMethod(APIRequest request) {
newServiceClient.newAPI(request.getName())
}
I want to do this functionality by using a custom annotation. I know that i can use interceptors to intercept this request and call the API. Is there any other way ?
Edit :
To summarise this question. There is a method(This might not be API start point. It can also be a normal method in your application) in my java code. Whenever i annotate this method, for every invocation of this method in application, i want do some functionality. I want to have this in annotation because i am thinking of providing a library for this annotation so that any function can be annotated
I am writing a service where I want to expose an endpoint which will call another service and if the service call is successful then I want to send back the result to UI/ calling app.
In parallel before sending back the response, I want to execute/submit a task which should run in background and my call should not be dependent on success or failure of this task.
Before returning the response i want to do the-
executorService.execute(object);
This should not be a blocking call..
Any suggestion
Spring Async methods is the way to go here as was suggested in comments.
Some caveats:
Async methods can have different return types, its true that they can return CompletableFuture but this is in case if you called them from some background process and would like to wait/check for their execution status or perhaps, execute something else when the future is ready. In your case it seems that you want "fire-and-forget" behavior. So you should use void return type for your #Async annotated method.
Make sure that you place #EnableAsync. Under that hood it works in a way that it wraps the bean that has #Async methods with some sort of proxy, so the proxy is actually injected into your service. So #EnableAsync turns on this proxy generation mechanism. You can verify that this is the case usually in the debugger and checking the actual type of the injected reference object.
Consider customizing the the task executor to make sure that you're running the async methods with executor that matches your needs. For example, you won't probably want that every invocation of async method would spawn a new thread (and there is an executor that behaves like this). You can read about various executors here for example
Update
Code-wise you should do something like this:
public class MyAsyncHandler {
#Async
public void doAsyncJob(...) {
...
}
}
#Service
public class MyService {
#Autowired // or autowired constructor
private MyAsyncHandler asyncHandler;
public Result doMyMainJob(params) {
dao.saveInDB();
// do other synchronous stuff
Result res = prepareResult();
asyncHandler.doAsyncJob(); // this returns immediately
return res;
}
}
I have written a interceptor to make some changes before service call in my spring boot rest service.
Added picture to show my code how I written preHandle method.
In the picture, I want to bring the "orginationTaskServerMockEnabled = true" value which is in
handler -> bean-> mHandler -> dashboardConfig-> originationTaskServerMockEnabled
I tried by it doesnt, is it possible to bring the values from HanlderMethod. If so please help me.
It is not possible to access the handler arguments in the interceptor's preHandle. It is called before the actual invocation of the handler method. The resolution of actual argument types is done during its invocation, so during the execution of preHandle, the argument's value are not resolved yet (but are described by Object handler argument). Only HttpServletRequest and HttpServletResponse are known during the execution so, if the data is attached to the request, you can grab it from there.
Alternatively, you can use an aspect and wrap it around your controller method. From there, you can grab the method arguments then perform your preprocessing.
#Around("execution (* com.pck.controllers.*.*(..)) && #annotation(org.springframework.web.bind.annotation.RequestMapping)")
public Object beforeHandler(ProceedingJoinPoint p){
Object args[] = joinpoint.getArgs();
return joinpoint.proceed();
}