Small question regarding how to "force" or "mock" the response of a Webclient http call during unit test phase please.
I have a very straightforward method which does:
public String question() {
String result = getWebClient().mutate().baseUrl(someUrlVariable).build().post().uri("/test").retrieve().bodyToMono(String).block();
if (result == null) {
doSomething1();
}
if (result.equals("")) {
doSomething2();
}
if (result.equals("foo")) {
doSomething3();
}
As you can see, the complex part of this method is the Webclient call. It has (in this example) 7 .method() like .mutate(), .post(). etc...
In my use case, I am not that interested testing this Webclient, at all.
What I would like to have with Mockito, is somehow the equivalent of:
public String question() {
// it is just unit test. Mockito, please just return me the string I tell you to return please. Don't even execute this next line if possible, just return me this dummy response
String result = the-thing-I-tell-mockito-to-return;
if (result == null) {
doSomething1();
}
if (result.equals("")) {
doSomething2();
}
if (result.equals("foo")) {
doSomething3();
}
So far, I tried Mockito doNothing(), or Mockito.when(getWebclient()... ) of the entire line plus .thenReturn, but no luck.
How to achieve such please?
I would like to avoid those copy/pasting of when()
Well you have designed your code so that the only way to test it is by copy pasting of when.
So how have you designed it? well you have mixed API-code with logic which is something you should not do. The first thing you need to think about when writing tests is "What is it i want to test?" and the answer is usually Business logic.
If we look at your code:
public String question() {
// This is api code, we dont want to test this,
// spring has already tested this for us.
String result = getWebClient()
.mutate()
.baseUrl(someUrlVariable)
.build()
.post()
.uri("/test")
.retrieve()
.bodyToMono(String)
.block();
// This is logic, this is want we want to test
if (result == null) {
doSomething1();
}
if (result.equals("")) {
doSomething2();
}
if (result.equals("foo")) {
doSomething3();
}
}
When we design an application, we divide it into layers, usually a front facing api (RestController), then the business logic in the middle (Controllers) and lastly different resources that call other apis (repositories, resources etc.)
So when it comes to your application i would redesign it, split up the api and the logic:
#Bean
#Qualifier("questionsClient")
public WebClient webclient(WebClient.Builder webClient) {
return webClient.baseUrl("https://foobar.com")
.build();
}
// This class responsibility is to fetch, and do basic validation. Ensure
// That whatever is returned from its functions is a concrete value.
// Here you should handle things like basic validation and null.
#Controller
public class QuestionResource {
private final WebClient webClient;
public QuestionResource(#Qualifier("questionsClient") WebClient webClient) {
this.webClient = webClient;
}
public String get(String path) {
return webClient.post()
.uri(path)
.retrieve()
.bodyToMono(String)
.block();
}
}
// In this class we make business decisions on the values we have.
// If we get a "Foo" we do this. If we get a "Bar" we do this.
#Controller
public class QuestionHandler {
private final QuestionResource questionResource;
public QuestionResource(QuestionResource questionResource) {
this.questionResource = questionResource;
}
public String get() {
final String result = questionResource.get("/test");
// also i dont see how the response can be null.
// Null should never be considered a value and should not be let into the logic.
// Because imho. its a bomb. Anything that touches null will explode (NullPointerException).
// Null should be handled in the layer before.
if (result == null) {
return doSomething1();
}
if (result.equals("")) {
return doSomething2();
}
if (result.equals("foo")) {
return doSomething3();
}
}
}
Then in your test:
#Test
public void shouldDoSomething() {
final QuestionResource questionResourceMock = mock(QuestionResource.class);
when(questionResourceMock.get("/test")).thenReturn("");
final QuestionHandler questionHandler = new QuestionHandler(questionResourceMock);
final String something = questionHandler.get();
// ...
// assert etc. etc.
}
Also, i suggest you don't mutate webclients, create one webclient for each api because it gets messy fast.
This is written without an IDE, so there might be compile errors etc. etc.
You have to first ensure that getWebclient() returns a mock. Based on your existing code example I can't tell if that's coming for a different class or is a private method (it might make sense to inject the WebClient or WebClient.Builder via the constructor).
Next, you have to mock the whole method chain with Mockito. This includes almost copy/pasting your entire implementation:
when(webClient.mutate()).thenReturn(webClient);
when(webClient.baseUrl(yourUrl)).thenReturn(...);
// etc.
Mockito can return deep stubs (check the documentation and search for RETURN_DEEP_STUBS) that could simplify this stubbing setup.
However, A better solution would be to spawn a local HTTP server for your WebClient test and mock the HTTP responses. This involves less Mockito ceremony and also allows testing error scenarios (different HTTP responses, slow responses, etc.),
Related
I kind of hit the wall with DeferredResult. We have really old pattern where we have Interfaces that contains all rest annotations and implementation of them. Also other clients (microservices) uses those interfaces to map communicate with each others (they are importing them as a module and make proxy rest calls). But there is a problem somebody hacked a bit this approach and we had two different declarations one for clients without DeferredResult and one with it on implementation side. When we tried to reflect changes for clients there is a problem a lot of them needs to change a way of communication. So i've been thinking of removing DeferredResult from method signature and just use result.
My question is how to do it in non blocking way in Spring?
Let's say i have this kind of code
#Component
public class ExampleSO implements ExampleSOController {
private final MyServiceSO myServiceSO;
public ExampleSO(MyServiceSO myServiceSO) {
this.myServiceSO = myServiceSO;
}
#Override
public DeferredResult<SOResponse> justForTest() {
CompletableFuture<SOResponse> responseCompletableFuture = myServiceSO.doSomething();
DeferredResult<SOResponse> result = new DeferredResult<>(1000L);
responseCompletableFuture.whenCompleteAsync(
(res, throwable) -> result.setResult(res)
);
return result;
}
}
where:
#RestController
public interface ExampleSOController {
#PostMapping()
DeferredResult<SOResponse> justForTest();
}
and:
#Component
public class MyServiceSO {
public CompletableFuture<SOResponse> doSomething() {
CompletableFuture<SOResponse> completableFuture = new CompletableFuture<>();
Executors.newCachedThreadPool().submit(() -> {
Thread.sleep(500);
completableFuture.complete(new SOResponse());
return null;
});
return completableFuture;
}
}
How could i achieve something like this:
#RestController
public interface ExampleSOController {
#PostMapping()
SOResponse justForTest();
}
Without removing async benefits ?
I have similar to below code in my application.
public String someMethod(final Object obj) {
final ValidationResponse validationResponse = new ValidationResponse();
String responseMessage = validatorService.validate(obj, validationResponse);
if(validationResponse.isValid()) {
//Positive flow
}
else {
//Negative flow
}
return responseMessage;
}
I am writing JUnit test cases to generate Mutation's report. As the object validationResponse is used which is a local object created in the flow. Mockito is unable to get & return desired value for isValid. Due to this I am unable to cover the test cases for the Positive flow.
How this could be achieved? Any lead is very much appreciated.
I got the solution to this problem from one of my teammate. It is as below
Mockito.doNothing(invocation ->
{
ValidationResponse validationResponse = invocation.getArgument(0);
validationResponse.setValida(true);//Set value true or false based on the mock test case scenario.
return null;
})
.when(validatorService)
.validate(obj, validationResponse));
This mocks the value of the validation response property.
The flow of this method is determined by this line
String response = validator1.validate(obj, validationResponse);
If you want to excercise both branches of the following if statment you need to control the modification that validator1 makes to the validationResponse object.
This can be done in two ways.
If your test can inject the instance of validator1 (eg via the code under tests constructor) then you can fake it.
This is probably easiest to do with a hand coded implementation rather than a mocking framework.
eg
class AlwaysInvalid implements WhateverTheTypeOfValidator1Is {
void validate(Object unused, ValidationResponse response) {
response.setInvalidOrWhatever();
}
}
Alternatively you can use a real collaborator, in which case your tests needs to ensure that objects are passed into someMethod that result in both valid and invalid responses.
Suppose I have a code like below
public boolean checkForecastIfCityRaining(String name){
result = WeatherAPICallToSomeVendor(name)
if(result = rain)return true; else return false;
}
How would I unit test if the result data will change depending
on what the API vendor is providing?
Would i mock a fixed result of every scenario and then unit test
it like so?
A UNIT test should really only test a single method at a time (Isolate other functionality that might be invoked by that method). My current group might achieve that by writing our function like this:
public class WeatherCheck
{
private ForecastService fs;
public WeatherCheck(ForecastService fs)
{
forecastService = fs;
}
public boolean checkForecastIfCityRaining(String name){
result = forecastService.weatherAPICallToSomeVendor(name)
if(result = rain)return true; else return false;
}
This would allow us to pass a mock forecast service into the constructor. Dependency Injection would be better, but we don't do that yet.
Note: We differentiate between a Unit test and an Integration test. We still might write our Integration tests with Junit, but they have more of a tendency to go out and actually poke the service--this can give you advance warning of a failure. So you might write an integration test for ForecastService that simply calls the weatherAPICallToSomeVendor method of ForecastService and ensures a non-error result (Perhaps no exceptions or doesn't return null...).
I think the function needs to be rewritten as this:
public boolean checkForecastInCityCondition(String city, String condition){
result = WeatherAPICallToSomeVendor(city)
return result == condition;
}
Now you gain the advantage of exposing clients to care about arbitrary conditions and you can enhance with a new API as needed. From a testing perspective you can now safely write tests like this:
public void testRainingInLancaster() throws Exception{
//your code here
}
public void testSnowInRedding() throws Exception{
//your code here
}
And you can determine which pieces need to be mocked for testing.
I've a webservice similar to the following:
#RequestMapping(value = "/getMovies", method = RequestMethod.POST, produces = "application/json")
public #ResponseBody ResponseVO getMoviesList(#RequestBody RequestVO vo) { .... }
The RequestVO class is :
public class RequestVO {
private String[] genreList;
public void updateRequest() {
if (genreList != null) {
// remove the duplicates from the list
// or something else
}
}
public String[] getGenreList() {
return genreList;
}
public void setGenreList(String[] genreList) {
this.genreList = genreList;
}
}
Now I want the method updateRequest to be called automatically after the request json is processed as RequestVO. One thing I currently think of is #PostConstruct, but seems to be of no use in this case.
My question is does Spring provide any such annotation or mechanism ? Or #PostConstruct will do the trick ?
NB : I don't need workarounds as I've plenty of them. So please refrain yourself from posting them. Again above codes are mere samples (please ignore minor mistakes).
Couple of thins to consider:
Don't use verbs in Rest Service method names (like getMovies) because you specify action using HTTP verbs like GET, POST and so on.
POST should be used to create a resource on the server not to retrieve them (what is implied by the method name: 'getMovies')
What do you want to achieve is RequestVO.updateRequest() invoked before passing RequestVO instance to getReportData(), is it right? If so, could you elaborate, why can't you invoke this method on the beginning of the getReportData()?
If you want to achieve this kind of functionality despite the fact it's sensible or not, try:
create new aspect which will be invoked before getReportData() and invoke updateRequest()
use #JsonFactory (provided you use Jackson to map JSON to Java objects) like:
public class RequestVO {
private String[] genreList;
public void updateRequest() {
if (genreList != null) {
...
}
}
public String[] getGenreList() {
return genreList;
}
public void setGenreList(String[] genreList) {
this.genreList = genreList;
}
#JsonFactory
public static RequestVO createExample(#JsonProperty("genreList") final String[] genreList) {
RequestVO request = new RequestVO(genreList);
request.updateRequest();
return request;
}
}
As you are saying, #PostConstruct is only call after a bean creation and is of no use here. But you have 2 simple ways of calling a method after the end of another method.
explicit : just wrap your real method in another one, and do all pre- or post-processing there
#RequestMapping(value = "/getMovies", method = RequestMethod.POST, produces = "application/json")
public #ResponseBody ResponseVO getMoviesList(#RequestBody RequestVO vo) {
// pre_processing
ResponseVO resul = doGetMoviesList(vo);
// post_processing
return resul;
}
public ResponseVO doGetMoviesList(RequestVO vo) { ... }
Is is simple to write, even if not very nice.
use Spring AOP. You can define an after returning advice that will be called after the advised method returns normally. The advice can be shared across multiple classes if you need it and write your pointcut accordingly. It is really powerfull, but has one caveat : Spring implementation uses proxies and by default JDK proxies. That means that any advised method should be member of an interface and called through that interface. So it would be much simpler and cleaner to advise a service than a controller. IMHO, if you really need to do AOP on a controller, you should use full AspectJ including class weaving ... In short, it is very nice, very powerfull, but a little harder to implement.
I would like to know what's the best approach to test the method "pushEvent()" in the following class with a jUnit test.
My problem is, that the private method "callWebsite()" always requires a connection to the network. How can I avoid this requirement or refactor my class that I can test it without a connection to the network?
class MyClass {
public String pushEvent (Event event) {
//do something here
String url = constructURL (event); //construct the website url
String response = callWebsite (url);
return response;
}
private String callWebsite (String url) {
try {
URL requestURL = new URL (url);
HttpURLConnection connection = null;
connection = (HttpURLConnection) requestURL.openConnection ();
String responseMessage = responseParser.getResponseMessage (connection);
return responseMessage;
} catch (MalformedURLException e) {
e.printStackTrace ();
return e.getMessage ();
} catch (IOException e) {
e.printStackTrace ();
return e.getMessage ();
}
}
}
Stubbing
You'll need a test double (stub) to allow isolated, easy, unit testing. The following is non tested, but demonstrates the idea. The use of Dependency Injection will allow you to inject at test time, a test version of your HttpURLConnection.
public class MyClass()
{
private IHttpURLConnection httpUrlConnection;
public MyClass(IHttpURLConnection httpUrlConnection)
{
this.httpUrlConnection = httpUrlConnection;
}
public String pushEvent(Event event)
{
String url = constructURL(event);
String response = callWebsite(url);
return response;
}
}
Then you create a stub (sometimes referred to as a mock object) to be the stand in for the concrete instance.
class TestHttpURLConnection : IHttpURLConnection { /* Methods */ }
You'll also construct a concrete version, for your production code to use.
class MyHttpURLConnection : IHttpURLConnection { /* Methods */ }
Using your test class (an adapter) you are able to specifiy what should happen during your test. A mocking framework will enable you to do this with less code, or you can manually wire this up. The end result of this for your test is that you'll set your expectations for your test, for example, in this case you may set OpenConnection to return a true boolean (This is just an example by the way). Your test will then assert that when this value is true, the return value of your PushEvent method matches some expected result. I've not touched Java properly for a while, but here are some recommended mocking frameworks as specified by StackOverflow members.
Possible solution: You can extend this class, override callWebsite (you have to make it protected for this purpose) - and the override method write some stub method implementation.
Approaching things from a slightly different angle...
I'd worry less about testing this specific class. The code in it is extremely simple and, while a functional test to make sure it's working with a connection would be helpful, a unit level test "may" not be necessary.
Instead, I'd focus on testing the methods it calls that appear to actually do something. Specifically...
I'd test constructURL method from this line:
String url = constructURL (event);
making sure that it can construct a URL properly from different Events, and throws Exceptions when it should (possibly on an invalid Event or null).
And I'd test the method from the following line:
String responseMessage = responseParser.getResponseMessage (connection);
Possibly pulling out any "get information out of the connection" logic into one proc, and leaving only "parse said information" in the original one:
String responseMessage = responseParser.getResponseMessage(responseParser.getResponseFromConnection(connection));
or something along those lines.
The idea being to put any "must deal with external data sources" code in one method, and any code logic in separate methods that can be easily tested.
As an alternative to Finglas's helpful answer with respect to mocking, consider a stubbed approach where we override the functionality of callWebsite(). This works quite well in the case where we aren't so interested in the logic of callWebsite as that of the other logic called within pushEvent(). One important thing to check is that callWebsite is calledwith the correct URL. So, first change is to the method signature of callWebsite() to become:
protected String callWebsite(String url){...}
Now we create a stubbed class like this:
class MyClassStub extends MyClass {
private String callWebsiteUrl;
public static final String RESPONSE = "Response from callWebsite()";
protected String callWebsite(String url) {
//don't actually call the website, just hold onto the url it was going to use
callWebsiteUrl = url;
return RESPONSE;
}
public String getCallWebsiteUrl() {
return callWebsiteUrl;
}
}
And finally in our JUnit test:
public class MyClassTest extends TestCase {
private MyClass classUnderTest;
protected void setUp() {
classUnderTest = new MyClassStub();
}
public void testPushEvent() { //could do with a more descriptive name
//create some Event object 'event' here
String response = classUnderTest.pushEvent(event);
//possibly have other assertions here
assertEquals("http://some.url",
(MyClassStub)classUnderTest.getCallWebsiteUrl());
//finally, check that the response from the callWebsite() hasn't been
//modified before being returned back from pushEvent()
assertEquals(MyClassStub.RESPONSE, response);
}
}
Create an abstract class WebsiteCaller which would be a parent of ConcreteWebsiteCaller and WebsiteCallerStub.
This class should have one method callWebsite (String url). Move your callWebsite method from MyClass to ConcreteWebsiteCaller. And MyClass will look like:
class MyClass {
private WebsiteCaller caller;
public MyClass (WebsiteCaller caller) {
this.caller = caller;
}
public String pushEvent (Event event) {
//do something here
String url = constructURL (event); //construct the website url
String response = caller.callWebsite (url);
return response;
}
}
and implement method callWebsite in your WebsiteCallerStub in some way appropriate for testing.
Then in your unit test do something like this:
#Test
public void testPushEvent() {
MyClass mc = new MyClass (new WebsiteCallerStub());
mc.pushEvent (new Event(...));
}