I am trying to do this java code in Dart/Flutter. It is a connection HttpRequestBase from a library called implementation "cz.msebera.android:httpclient:4.4.1.2".
Java example: Inside an activity called HttpPut.java there is a call to this library. Here is the specific part that I want to take to Dart.
protected HttpRequestBase getRequestBase(Request request) throws UnsupportedEncodingException {
cz.msebera.android.httpclient.client.methods.HttpPut httpPost = new cz.msebera.android.httpclient.client.methods.HttpPut(
request.getResource()
);
StringEntity entity = new StringEntity(request.getParameters(), "UTF-8");
entity.setContentType(new BasicHeader("Content-Type", "application/json"));
httpPost.setEntity(entity);
return httpPost;
}
The Request class contains the following:
import java.util.Collection;
public interface Request {
String getResource();
String getParameters();
Collection<String> getHeaders();
What I have tried in Dart is to call the library http: ^ 0.12.1 but it is not exactly what I need. Because although I can do, in this case httpPut(...), I cannot perform the following steps such as StringEntity. How would you solve those problems?
While Flutter does allow you to insert platform-specific code in your app, this is probably not what you want in this case. The classes/methods involved will not be the same, but you should be able to achieve what your Android code does with other classes/methods from Dart. Check out the HttpClient class. Your code might be similar to this (though this snippet is missing request.getParameters(), since I'm not sure what that changes):
Future<HttpClientRequest> getRequestBase(Request request) async {
HttpClientRequest httpRequest = await HttpClient().putUrl(Uri.parse(request.getResource()));
httpRequest.headers.contentType = ContentType('aplication', 'json', charset: 'UTF-8');
return httpRequest;
}
Then, when you want to actually send the request in your code and get the response, you can do
HttpClientRequest requestBase = await getRequestBase(request);
HttpClientResponse response = await requestBase.close();
Related
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);
I'm writing function that will be used with HttpGet and HttpPost.
It's something like that:
private void initialize(HttpRequestBase method)
{
if(method == new HttpPost())
{
String body = "body";
HttpEntity entity = new ByteArrayEntity(body.getBytes("UTF-8"));
method.setEntity(entity);
}
Problem is that HttpRequestBase doesn't support .setEntity. How can I write function that support HttpGet and HttpPost without problems like that?
The way to work around this problem is to check the type using instanceof and use a cast. Like this:
private void initialize(HttpRequestBase method)
{
if(method instanceof HttpPost)
{
String body = "body";
HttpEntity entity = new ByteArrayEntity(body.getBytes("UTF-8"));
((HttpPost) method).setEntity(entity);
}
}
But whenever you use a cast, you should consider that there may be a more elegant solution. In this case, I would argue that a more elegant solution would be to use method overloading and have a specific method for HttpPost instances.
private void initialize(HttpPost method)
{
String body = "body";
HttpEntity entity = new ByteArrayEntity(body.getBytes("UTF-8"));
method.setEntity(entity);
}
Of course this would mean you would need a separate method for HttpGet (and any other subclasses of HttpRequestBase you wish to support). Any common code shared between GET and POST should be extracted into smaller methods that are called by both initialize() methods.
private void initialize(HttpGet method)
{
// ...
}
This, of course, you might argue defeats the whole point of trying to create one handler for both GET and POST. And you would be right. But you should then question the entire exercise of trying to create one method that handles both. Perhaps the most elegant design is to treat them separately. After all, that is precisely what the authors of the framework you're using chose to do.
I am trying to connect to an API of another company.
from the doc there is ::
even with your GET request, you'll need to include the Java equivalent of
curl_setopt($ch, CURLOPT_POSTFIELDS, $content), and you can set $data equal
to an empty array.
$content in their example is an empty JSON array.
I am using org.apache.commons.httpclient.
i am not sure how to add post fields to a org.apache.commons.httpclient.methods.GetMethod or if it is even possible.
i tried faking with a Content-Length of 2 but the GET times out (probably looking for content that i am not providing. if i remove the content-length i get an invalid response from the api server)
HttpClient client = new HttpClient();
GetMethod method = new GetMethod("https://api.xxx.com/account/");
method.addRequestHeader("Content-Type", "application/json");
method.addRequestHeader("X-Public-Key", APKey);
method.addRequestHeader("X-Signed-Request-Hash", "xxx");
method.addRequestHeader("Content-Length", "2");
int statusCode = client.executeMethod(method);
I don't think GetMethod includes any means of attaching a request body, because a GET request isn't supposed to have a body. (But having a body isn't actually prohibited, either - see: HTTP GET with request body .)
You're trying to use documentation written with a different language and a different client library in mind, so you'll have to use trial and error a bit. It sounds like they expect a request with no body, and you already have that. There's no good reason why they'd require a "Content-Length" with GET, but if that's the case, try setting it to 0.
This is how i resolved this issue
Created this class
public class HttpGetWithEntity extends HttpEntityEnclosingRequestBase {
public HttpGetWithEntity() {
super();
}
public HttpGetWithEntity(URI uri) {
super();
setURI(uri);
}
public HttpGetWithEntity(String uri) {
super();
setURI(URI.create(uri));
}
#Override
public String getMethod() {
return HttpGet.METHOD_NAME;
}
}
Then the calling function looks like
public JSONObject get(JSONObject payload, String URL) throws Exception {
JSONArray jsonArray = new JSONArray();
CloseableHttpClient client = HttpClientBuilder.create().build();
HttpGetWithEntity myGet = new HttpGetWithEntity(WeeblyAPIHost+URL);
myGet.setEntity( new StringEntity("[]") );
myGet.setHeader("Content-Type", "application/json");
myGet.setHeader("X-Public-Key", APIKey);
HttpResponse response = client.execute(myGet);
JSONParser parser = new JSONParser();
Object obj = parser.parse( EntityUtils.toString(response.getEntity(), "UTF-8") ) ;
JSONObject jsonResponse = (JSONObject) obj;
return jsonResponse;
}
I have no idea before how to write the test cases, when i saw online tutorials i understand how to write it for a simple method with success and failure scenario. Now i have a method for http get which calls a restful API and returns a json response. I have like 6 parameters to include in the url and get a json response back. Now, my understanding so far is for success scenario here i should just hard code those input parameters and test if i am getting json back and for failure not getting json response back. Is this correct or do i have to do something else?
i mean i have a code something like
public List getStoreLocations(StoreData storeData) {
List storeList = null;
try {
HttpClient httpclient = HttpClientBuilder.create().build();
StringBuilder urlStrngBuildr = new StringBuilder(
https://<hostname>/xyz/abc);
Utility.addParameterToUrl(urlStrngBuildr,
Utility.APP_NAME,
Constants.APP_VALUE);
Utility.addParameterToUrl(urlStrngBuildr,
Constants.VERSION_PARAM_NAME,
Constants.VERSION_PARAM_VALUE);
if (storeData.getCity() != null && storeData.getState() != null) {
StringBuilder addressParamValue = new StringBuilder(
storeData.getCity());
addressParamValue.append(Constants.COMMA);
addressParamValue.append(storeData.getState());
Utility.addParameterToUrl(urlStrngBuildr,
Constants.ADDRESS_PARAM_NAME,
addressParamValue.toString());
} else if (storeData.getZip() != null) {
Utility.addParameterToUrl(urlStrngBuildr,
Constants.ZIP_PARAM_NAME, storeData.getZip());
}
Utility.addParameterToUrl(urlStrngBuildr,
Constants.PRODUCT_PARAM_NAME,
storeData.getProduct());
Utility.addParameterToUrl(urlStrngBuildr,
Constants.COUNTRY_PARAM_NAME,
storeData.getCountry());
Utility.addParameterToUrl(urlStrngBuildr,
Constants.DISTANCE_PARAM_NAME,
storeData.getDistance());
Utility.addParameterToUrl(urlStrngBuildr,
Constants.SIZE_PARAM_NAME, storeData.getSize());
HttpGet getRequest = new HttpGet(new java.net.URI(
urlStrngBuildr.toString()));
getRequest.addHeader(BasicScheme.authenticate(
new UsernamePasswordCredentials(username,password),
Constants.ENCODING_TYPE, false));
JSONResponseHandler responseHandler = new JSONResponseHandler();
String json = httpclient.execute(getRequest, responseHandler)
.toString();
Gson gson = new Gson();
StoreResponse response = gson.fromJson(json,
StoreResponse.class);
StoreDetails[] strDetails = response.getResult();
storeDetailsList = Arrays.asList(strDetails);
} catch (Exception exeption) {
exeption.printStackTrace();
}
return storeList;
}
Maybe you should take a look at REST-assured, which is a REST API testing framework.
The nice thing is, that it is much easier to read, supports JSON and XML and allows you to test things like HTTP-Codes or specific values from the response.
get("/lotto")
.then()
.assertThat().body("lotto.lottoId", equalTo(5));
You could add your parameters with the param method:
given()
.param("key1", "value1")
.param("key2", "value2")
when().
aso...
If you need authentication, like in your code, you can just use something like the following:
given()
.auth()
.basic(username,password)
.when()
.get("/secured")
.then()
.statusCode(200);`
Hope this helps with your testing.
It looks like the main thign you need to mock on that method is the HTtpClient. So how about you create a method for getting the client, then mock that method so that it returns a mock HttpClient.
public HttpClient getHttpClient(){
return HttpClientBuilder.create().build();
}
Then in your method you will do:
HttpClient httpclient = getHttpClient();
Then in your unit test code you will mock the getHttpClient method like so..
HttpClient mockClient = mock(HttpClient.class);
MyClassBeingTested instance = spy(new MyClassBeingTested ());
when(instance .getHttpClient()).thenReturn(mockClient);
when(mockClient.execute(any(HttpGet.class),any(JSONResponseHandler.class)).thenReturn(testJsonString);
List actual = instance.getStoreLocations(storeData);
Something like that.
I need to be able to set an entity on an OPTIONS call but looks like HttpClient does not support it. HttpPost and HttpPut extend from HttpEntityEnclosingRequestBase, while HttpOptions does not.
Anyone know the reason for this, or if there is a way around this?
The HTTP specification states
If the OPTIONS request includes an entity-body (as indicated by the
presence of Content-Length or Transfer-Encoding), then the media type
MUST be indicated by a Content-Type field. Although this specification
does not define any use for such a body, future extensions to HTTP
might use the OPTIONS body to make more detailed queries on the
server. A server that does not support such an extension MAY discard
the request body.
The Apache Http Client team probably decided that there was no use case that would warrant a request body in a OPTIONS request.
In case anyone needs this, here is how did this for HttpClient.
#Autowired
HttpClient httpClient;
public HttpResponse execute(String url, String json, String accessToken) {
HttpOptionsWithBody httpOptionsWithBody = new HttpOptionsWithBody(url);
httpOptionsWithBody.setEntity(new StringEntity(json));
return httpClient.execute(httpOptionsWithBody);
}
private static class HttpOptionsWithBody extends HttpEntityEnclosingRequestBase {
public static final String METHOD_NAME = "OPTIONS";
#Override
public String getMethod() {
return METHOD_NAME;
}
public HttpOptionsWithBody(final String uri) {
super();
setURI(URI.create(uri));
}
}
I got the idea on how to do this from HttpDelete with body