I would like to test an application that connects to Github api, download some records and do something with them.
I want to have a mock object and I did something like that:
#SpringBootTest
public class GithubApiTest
{
GithubApiClient githubApiClient;
#Mock
HttpClient httpClient;
HttpRequest httpRequest;
#Value("response.json")
private String response;
#BeforeTestClass
void init() throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.GET()
.uri(URI.create("https://api.github.com/repos/owner/reponame"))
.build();
githubApiClient = new GithubApiClient(httpClient);
Mockito.when(httpClient.send(request, HttpResponse.BodyHandlers.ofString())).thenReturn(<here>
);
githubApiClient = new GithubApiClient(httpClient);
}
}
Looks it good? What should I put into thenReturn (it needs a HttpResponse but I dont know how to create this). Thanks for your answers. If you have any better ideas I will be grateful.
Update:
String response is a example reponse
You create a mocked response of type HttpResponse.
The mockedResponse will have status code 200, and the response body is "ExampleOfAResponseBody" which is of type String which you requested.
import java.net.http.HttpResponse;
HttpResponse<String> mockedResponse = Mockito.mock(HttpResponse.class);
Mockito.when(mockedResponse.statusCode()).thenReturn(200);
Mockito.when(mockedResponse.body()).thenReturn("ExampleOfAResponseBody");
Mockito.when(httpClient.send(request, HttpResponse.BodyHandlers.ofString())).thenReturn(mockedResponse);
Related
I'm trying to setup a contract test with Pact (so far only Consumer-side).
This is what my code looks like:
#Pact(consumer = "Consumer")
RequestResponsePact apiIsReachablePact(PactDslWithProvider builder) {
return builder.given("api is reachable")
.uponReceiving("load api")
.method("GET")
.path("/?format=json")
.willRespondWith()
.status(200)
.headers(headers())
.body(newJsonBody(object -> {
object.stringType("ip", "XYZ");
}).build())
.toPact();
}
#Test
#PactTestFor(pactMethod = "apiIsReachablePact")
public void apiIsReachable() throws IOException {
//Given
HttpUriRequest request = new HttpGet("https://api.ipify.org");
//When
HttpResponse httpResponse = HttpClientBuilder.create().build().execute(request);
//Then
assertEquals(httpResponse.getStatusLine().getStatusCode(), HttpStatus.SC_OK);
}
I tried to make it as simple as possible, but I receive the following error:
au.com.dius.pact.consumer.PactMismatchesException: The following requests were not received:
method: GET
path: /?format=json
query: {}
headers: {}
matchers: MatchingRules(rules={})
generators: Generators(categories={})
body: MISSING
...
Could anyone please help me along here?
Pact doesn't intercept your requests, so this call doesn't actually talk to the Pact mock server, hence why it was not received - it's going to the real API:
HttpUriRequest request = new HttpGet("https://api.ipify.org");
You don't test against real API in Pact in the consumer test, you mock out the provider and test using the Pact Mock. It will then generate a contract that the provider can then use to verify your expectations:
It should be:
#Test
#PactTestFor(pactMethod = "apiIsReachablePact")
public void apiIsReachable(MockServer mockServer) throws IOException {
//Given
HttpUriRequest request = new HttpGet(mockServer.getUrl());
//When
HttpResponse httpResponse = HttpClientBuilder.create().build().execute(request);
//Then
assertEquals(httpResponse.getStatusLine().getStatusCode(), HttpStatus.SC_OK);
}
See this example and this workshop for more.
Im currently in a task in which i have to send a GET request with a body. Im a aware this isn't a good practice and that i should send the json through query params.
But I'm bound to do it like this.
So let's continue. I use RestTemplate with exchange but due to SimpleClientHttpRequestFactory implementation i cannot send a body with a GET method.
RestTemplate template = new RestTemplate(new CustomClientHttpRequestFactory());
httpHeaders.setContentType(APPLICATION_JSON);
httpHeaders.set("token", token.getToken());
httpHeaders.set("companyId", companyId);
URI uri = new URI(getInspectionsUrl);
HttpEntity<InspectionsInputDTO> entity = new HttpEntity<InspectionsInputDTO>(inputDTO, httpHeaders);
response = template.exchange(uri, GET, entity, InspectionsResponseDTO.class);
After some research i found the following code:
class CustomClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
#Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
super.prepareConnection(connection, httpMethod);
if ("GET".equals(httpMethod)) {
connection.setDoOutput(true);
}
}
}
// RestTemplate initialization
RestTemplate template = new RestTemplate(new CustomClientHttpRequestFactory());
This tries to override SimpleClientHttpRequestFactory httpMethod allowance but id does not work. The question is, how can i send a Request BODY in GET request with RestTemplate. Maybe there is another way to override SimpleClientHttpRequestFactory. Im new in this strange world of spring, sorry if im saying something wrong (:
I have a method in class which does a HTTP GET call to get the response object and utilize this object further. The pesudo code is below:
public class ABC{
public method abc1(){
HttpUrl url = HttpUrl.parse("url").newBuilder()
.addPathSegment("path1")
.build();
Request request = new Request.Builder().url(url).build();
try (Response response = client.newCall(request).execute()) {
ResponseBody responseBody = response.body();
String body = responseBody.string();
//other logic
}catch (IOException e) {}
}
}
Now I am writing a unit test to test with different values in the response object (json object). This is as below:
public class ABCTest{
#Mock
private OkHttpClient mockHttpClient;
#Mock
private Call mockCall;
#Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
#Test
public void abc1Test(){
ResponseObjectInJson responseObjectInJson = new ResponseObjectInJson(); //this is a object from my POJO class that i create in order to be received as a response
JSONObject jsonObject = new
JSONObject(responseObjectInJson);
ResponseBody body =
ResponseBody.create(MediaType.parse("application/json"),new
Gson().toJson(jsonObject));
Response.Builder builder = new Response.Builder();
builder.code(200);
Response response = builder.body(body).build();
when(mockCall.execute()).thenReturn(response);
when(mockHttpClient.newCall(any(Request.class))).thenReturn(mockCall);
//call the abc1() method here to see the response and behaviour
}
}
The problem is, when i debug, it throws InvocationTargetException when building the response builder.body(body).build();
And shows java.lang.IllegalStateException: request == null. I understand that i need to set request in the Response.Builder because when i evaluate the expression builder.body(body) in debugger, in the result it shows headers and body, but request is null.
i.e., builder.request(//a request here)
My question is:
1. In response why the request is needed?
2. How to set this? because i am unable to mock since its final.
Thanks in advance
I have a class annoted with Service Annotation on server 1 .
#Service
public class MainHandler implements AbstractHandler {
#Autowired
private ServiceLocal defaultService;
#Override
public boolean execute(HttpServletRequest request, HttpServletResponse response) throws MsisdnServiceException {
System.out.println("The default Request" + request);
}
}
I want to call this method from other remote server after passing the request and get the response from this , what is the way to do in spring .
Invoking methods remotely would be using a technology called RMI, which you can google easily.
However, since you want to use HttpServletRequest and HttpServletResponse, you probably should write an Http Controller using Spring MVC. For that you can also google and very easily find excellent tutorials and guides.
You can use spring's RestTemplate to make communication with the servers.
First you need to create a controller on server 1 backend to get data from server 2:
#RestController
public class MyController {
#RequestMapping(value = "/endpoint", method = RequestMethod.POST)
String execute(#RequestBody MyClass object) {
System.out.println("Your data" + object);
}
}
On server 2 backend create a method that make a REST call to server 1's endpoint with RestTemplate:
void request() {
String url = "http://localhost:8080/endpoint";
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("Content-Type", "application/json");
JSONObject json = new JSONObject();
json.put("name", "yourName");
json.put("email", "name#gmail.com");
HttpEntity < String > httpEntity = new HttpEntity < String > (json.toString(), httpHeaders);
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.postForObject(url, httpEntity, String.class);
}
I have 2 Spring Web applications: Application1 and Application2. In Application1, I have an endpoint at "http://application1/getbigcsv" that uses streaming in order to serve a gigantic 150MB CSV file back to the user if they hit that URL.
I dont want users to hit Application1 directly, but hit Application2 instead.
If I have the following method in my controller in Application2
#RequestMapping(value = "/large.csv", method = GET, produces = "text/csv")
#ResponseStatus(value = HttpStatus.OK)
public String streamLargeCSV() {
// Make an HTTP Request to http://application1/getbigcsv
// Return its response
}
My worry is the above is not doing "streaming" whereas Application1 is doing streaming. Is there some way I can make sure that the application2 will be serving back the same data from application1's rest endpoint in a streaming fashion? Or is the method above actually returning things in a "Streaming" method already because Application1 is serving its endpoint as streaming?
First of all: you can but not with that method signature.
Unfortunately, you have not shown how you produce that CSV file in app1, whether this is truly streaming. Let's assume it is.
You signature will look like this:
#RequestMapping(value = "/large.csv", method = GET, produces = "text/csv")
#ResponseStatus(value = HttpStatus.OK)
public void streamLargeCSV(OutputStream out) {
// Make an HTTP Request to http://application1/getbigcsv
// Return its response
}
Now we have to grab the input stream from app1 first. Use Apache HttpClient to get your HttpEntity. This entity has a writeTo(OutputStream) method which will receive your out parameter. It will block until all bytes are consumed/streamed. When you are done, free all HttpClient resources.
Complete code:
#RequestMapping(value = "/large.csv", method = GET, produces = "text/csv")
#ResponseStatus(value = HttpStatus.OK)
public void streamLargeCSV(OutputStream out) {
// Make an HTTP Request to http://application1/getbigcsv
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("http://application1/getbigcsv");
CloseableHttpResponse response = httpclient.execute(httpGet);
try {
HttpEntity entity = response.getEntity();
// Return its response
entity.writeTo(out);
} finally {
response.close();
}
}
Here is my real world example. Start reading from "Interesting to say what I have achieved in particular with this:"
In java.ws.rs.core package you have classes: StreamingOutput and ResponseBuilder.
Not sure if it will help you, but you may try.
Example:
#Produces("application/octet-stream")
public Response doThings () {
...
StreamingOutput so;
try {
so = new StreamingOutput() {
public void write(OutputStream output) {
…
}
};
} catch (Exception e) {
...
}
ResponseBuilder response = Response.ok(so);
response.header("Content-Type", ... + ";charset=utf-8");
return response.build();
}
Change your methods return type to ResponseEntity<?> and return as following:
#GetMapping("/download")
public ResponseEntity<?> fetchActivities(
#RequestParam("filename") String filename) {
String string = "some large text"
InputStream is = new ByteArrayInputStream(string.getBytest());
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=large.txt");
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);
return ResponseEntity.ok().headers(headers).body(new InputStreamResource(is));
}