I am trying to unit test a class that uses Jersey 2 Client + Moxy to call a REST service. I want to mock the response, which contains a large JSON object. The code is structured in such a way that I can override/mock the following method:
protected Response doPost(String path, Entity<?> entity) {
Invocation.Builder invocationBuilder = getRestInvocationBuilder(path);
Response response = invocationBuilder.post(entity);
return response;
}
I would like to somehow inject some example JSON data (ideally from a file) into the Response at this point, prior to readEntity() being called, so that I can test that the JSON data is correctly unmarshalled into the target object.
Is there any way to do this? Note that this is for unit testing and therefore I'm not interested in running a local server or other integration testing techniques.
I'm aware similar questions have been asked, but many seem out of date or have incomplete solutions. The closest solution suggested is to mock the readEntity() method of the Response, this will not work for me because it would involve creating an object of the desired type to return, rather than creating one from the example JSON data.
Related
I'm new to Java programming and I have the following snippet on which I want to write unit test:
Response response = request.get();
if (response.getStatusInfo().getFamily().equals(Response.Status.Family.SUCCESSFUL)) {
return response.readEntity(type);
}
I'm able to create the scenario where HTTP request returns a valid response using the below code:
stubFor(get("someUrl").willReturn(aResponse().withStatus(200)));
I want to create another scenario where the method call response.readEntity(type) throws an exception. For this, I require that request.get() method returns me a mocked object so that I can define the desired behavior on the mocked object.
I read the documentation provided at http://wiremock.org/docs to find how to do this behavior but didn't find any way to return a mocked object as HTTP response.
Also, the request variable is not injected and hence I can't mock it directly.
You cannot do something like
stubFor(get("/$metadata?annotations=true").willReturn(aResponse().withStatus(200).withBody(Mock()));. It is because wiremock acts only as http server mock. Only thing you can configure is response (ex. in JSON).
What you can do is to return for example 400 and error code body from wiremock and check if you code accepts this message and act on it correctly.
Background
We are consuming the API from a 3rd-party vendor
Problem Statement:
I am building a wrapper API around another API. The same exact JSON payload that I will be receiving from the client to the wrapper API will also be used to make an HTTP request to the original API.
Currently I'm converting JSON which deserializes to a String. Is this the right approach if the payload is just passing through the wrapper API to the original API? In other words, is the #RequestBody type String okay for my use case or do I still need to deserialize to a Java Object?
Use Case for wrapper
If multiple teams consumed the API from the 3rd-party vendor, all teams would have to make changes if we were to switch vendors. If we create a wrapper, only one team would have to make the changes. There is no processing in this wrapper.
Controller Code:
#RestController
#RequestMapping(value = FolderController.PATH, produces = MediaType.APPLICATION_JSON_VALUE)
public class PersonController(){
static final String PATH = "/person";
private final PersonService personService;
#Autowired
public PersonController(PersonService personService){
this.personService = personService
}
#PostMapping
#ResponseBody
public String createPerson(#RequestBody String requestBody){
return personService.createPerson(requestBody);
}
Whether you need to deserialize depends on what processing is necessary for your wrapper. If you just want to pass the request further without changing it, this should work.
However, if you just need to proxy the request, consider using Smiley's HTTP Proxy Servlet for this task.
Or if you are wrapping the API to implement security around it, then consider using Spring cloud gateway
It is always better to follow coding practices and convert the request body to a java POJO class instead of a string. Converting to POJO has several advantages :
You could provide additional validations for the request body.
Incase the request body changes or if there are any issues it could be identified easily.
Provides better control over the request structure.In future , if the API that you are consuming changes the request body structure, you could change it from your API instead of adapting it at every API that consumes your API.
I have a Jersey client which performs post request to some black box service.
Also I have POJO mapping feature enabled.
I have integration tests already, they are calling real black box service.
Now I need to test my application without calling real black box service.
My question is: how can I test this Jersey client? I mean: how can I test Jersey client without calling real black box service?Maybe there is some possibility to mock JSON response in tests?
Environment: jersey-client and jersey-json versions - 1.19.1.
You can log the request using LoggingFilter to make sure your requests are according to your estimate. For running your test, you can use http://mockable.io/.
P.S. Do not forget to replace the Webtarget URL with MockableIO's URL.
You can try Karate which has recently introduced a way to create simple mocks for JSON responses. Here is an example:
https://gist.github.com/ptrthomas/35ef9d40623cbeade7388b2cbb29a3b1
While the above example is a "smart" mock, it is very easy to create hard-coded mocks, here is an example: https://github.com/intuit/karate/blob/master/karate-netty/src/test/java/com/intuit/karate/mock/_mock.feature
You can easily read files from JSON by using the read keyword.
I have performed an investigation concerning my question. Mentioned investigation resulted in such facts:
I haven't found features/mechanisms/etc. to test Jersey client without real calls to the server (in my case - black box service).
Jersey test framework provides features for testing Jersey server, but there are no features for testing Jersey client
The only one solution for testing client without server is to refactor my code in following way: split logic into two phazes. First phaze: get JSON response using Jersey. Second phaze: get JSON response (which we get in first phaze) and transform it into desired object. In general you'll have next code as a result:
Class< String > jsonResposeClass = String.class;
String jsonResponse = post( yourRequest, jsonResposeClass );
ObjectMapper mapper = new ObjectMapper();
YourResponseBean bean = mapper.readValue( jsonResponse , responseClass );
The solution above will give you a possibility to use mocking libraries like Mockito and mock method which performs post request.
If you have found any other solution/feature/special mechanism/etc which can also help in such kind of situation - please share :)
I'm using Jersey 2.22 to consume a REST api.
The approach is contract-first, a service interface and wrappers are used to call the REST api (using org.glassfish.jersey.client.proxy package).
WebClient webClient = ClientBuilder.newClient();
WebTarget webTarget = webClient.getWebTarget(endPoint);
ServiceClass proxy = WebResourceFactory.newResource(ServiceClass.class, webTarget);
Object returnedObject = proxy.serviceMethod("id1");
The question is: how to get the underlying HTTP response (HTTP status + body)?
When the returnedObject is null, I need to analyze the response to get error message returned for example.
Is there a way to do it?
I saw that we can plug filters and interceptors to catch the response, but that's not exactly what I need.
You should return Response as the result of the interface method instead of the plain DTO.
I'm not sure about the level of control you're expecting (considering your reply to #peeskillet comment), but the Response object will give you the opportunity to fine tune your server's response (headers, cookies, status etc.) and read all of them at the client side - as you might see taking a look at Response's members like getStatus() and getHeaders().
The only gotcha here is how to get the body. For this, I'd tell you to use readEntity(Class<T>) (not the getEntity() method as one might try at first). As long as you have the right media type provider registered, you can extract the entity as your DTO class in a easy way.
For example, if you are using maven, jersey and JSON as media type, you can add the following dependency (and take the provider's registration for granted):
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
</dependency>
Then, get the entity body deserialized using:
Response resp = proxy.serviceMethod("id1");
int status = resp.getStatus();
String statusText = resp.getStatusInfo();
String someHeader = resp.getHeaderString("SOME-HEADER");
YourCustomDTO obj = resp.readEntity(YourCustomDTO.class);
When querying a list of your custom objects (i.e. method returns a JSON array) use the array type to read the body.
Response resp = proxy.serviceMethodThatReturnsCollection();
YourCustomDTO[] obj = resp.readEntity(YourCustomDTO[].class);
Please notice that after reading the body, the stream is closed and trying getEntity() may throw an exception.
Hope it helps.
I am creating a Web Service that will handle incoming request properly (I knew, it is a description) I found some sample code which present SOAP WS and looks like:
#SoapAction("some fine url")
#ResponsePayload
public CertResponse getCert(#RequestPayload Cert param)
{...}
It takes Cert object from request and manages it properly. I want to do it in the REST approach and I changed this class to look like:
#RequestMapping(value="getCert", method = RequestMethod.POST)
#ResponseBody
public CertResponse getCert(#RequestBody Cert param)
{...}
But I have no idea how or even if it is possible to write a client that can send object through HTTP Post.
Can anyone give me a hint how can i send object Cert? Or if i have to stay with #SoapAction what should I do to make it work? I guess removing #Controller is not enough.
In SOAP approach, there is a well defined way to convert each class object to SOAP formatted XML. Thus, there is no effort.
If you will use RESTful approach, you have to describe how your Cert or CertResponse objects will be written to/read from the response/request.
Basically you have three options:
Use JSON or XML or plain String. Convert your Web service descriptor so that the request and response are one of those (JSON, XML, String). Then your getCert method should convert the request to Cert object, prepare your response as CertResponse object and convert it to an appropriate response type.
Leave your getCert method as is. But you need to specify "how Cert objects are read from request" and "how CertResponse objects are written to response" You need to define classes that extend interfaces MessageBodyReader and MessageBodyWriter. In these classes a similar conversion logic should be implemented that converts your objects to/from JSON or XML or String.
In any of these two options you need to implement two things : A method that converts JSON/XML/String to Cert, a method that converts CertResponse object to JSON/XML/String.
You can do a text based implementation, which parses/constructs the request/response by text processing and uses String class only. Or you can use some library such as JSON library with JSONObject class, or Java SAX or DOM based XML libraries that come with the Java bundle.
Check out for application frameworks such as Spring. They might provide ways to automatically convert your objects to JSON or XML, reducing the programming effort necessary.
Spring allows you to pass JSON objects from the client as a request parameters, it will convert them into your objects automatically. This discussion have some examples.
SOAP services are not really compatible with REST semantics. And it's not clear what SOAP framework do you use. Typically most SOAP frameworks offer you one way or another to generate a SOAP client code for you WSDL. You can check cxf.apache.org.
For REST services use something like Jersey or Spring MVC