How to mock HttpClient.newHttpClient() in Junit - java

public String sendAPICall(String portalApiUrl, String reviewAppsAuthKey) {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI(portalApiUrl + CHECK_ORG_ATT_EXP))
.setHeader(AUTH_REVIEW_API_KEY, reviewAppsAuthKey)
.setHeader(CONTENT_TYPE, APPLICATION_JSON)
.GET()
.build();
HttpClient httpClient = HttpClient.newHttpClient();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == HttpStatus.OK.value()) {
LOGGER.info(API_SUCCESS);
return API_SUCCESS;
}
} catch (URISyntaxException e) {
LOGGER.error(API_FAIL_URI, e.getMessage());
} catch (InterruptedException e) {
LOGGER.error(API_FAIL_INTER, e.getMessage());
} catch (IOException e) {
LOGGER.error(API_FAIL_IO, e.getMessage());
}
return API_FAILURE;
}
}
Junit :
#Test
public void sendAPICallTest() throws IOException, InterruptedException {
Checkorg test = getCheckOrg();
String portalApiUrl = JUNIT_PORTAL_URL;
String reviewAppsAuthKey = JUNIT_AUTH_KEY;
String message = test.sendAPICall(portalApiUrl, reviewAppsAuthKey);
assertEquals(Checkorg.API_SUCCESS, message);
}
How to mock the HTTP Client in Test Class.
Thank you

As per your comment:
how can we test this method for success if we can't mock
you can use spy for that :
#Test
public void sendAPICallTest() throws IOException, InterruptedException {
Checkorg test = getCheckOrg();
String portalApiUrl = JUNIT_PORTAL_URL;
String reviewAppsAuthKey = JUNIT_AUTH_KEY;
Checkorg mockedTest = Mockito.spy(test);
Mockito.when(mockedTest.sendAPICall(portalApiUrl,reviewAppsAuthKey)).thenReturn("API Call Success");
String message = mockedTest.sendAPICall(portalApiUrl, reviewAppsAuthKey);
assertEquals(Checkorg.API_SUCCESS, message);
}
This can be a quick workaround if HttpClient mocking is not feasible.

Related

Mocking a Nested Object in Junit

I have a service I am mocking like this:
#ExtendWith(MockitoExtension.class)
class MyServiceTest {
#InjectMocks
MyService myService;
#Test
void testSendRec() {
myService.sendDocRec(.. pass params..);
}
}
the service:
#Service
public class MyService {
String sendDocRec( params ) {
// builds request
HttpUriRequestBase request = getRequest( params );
String response = doRequest(request);
}
public String doRequest(ClassicHttpRequest request) {
String result = null;
try (CloseableHttpClient httpclient = HttpClients.custom()
.setConnectionManager(this.connectionManager)
.setConnectionManagerShared(true)
.setDefaultRequestConfig(this.requestConfig)
.build()) {
final HttpClientContext clientContext = HttpClientContext.create();
try (CloseableHttpResponse response = httpclient.execute(request, clientContext)) {
result = EntityUtils.toString(response.getEntity());
}
} catch (URISyntaxException e) {
log.error("Invalid URI {}", e);
} catch (IOException e) {
log.error("Failed to make HTTP Request {}", e);
} catch (ParseException e) {
log.error("Failed parsing response body {}", e);
}
return result;
}
}
I need to be able to mock the "CloseableHttpResponse response = httpclient.execute(request, clientContext)", such that the "response" object is something I create ahead of time. I am hoping some mocking when/then constructs would work for this? I would grateful for ideas on how to do this. Thanks!
You can't do it using the current code structure. httpclient object is getting created within the method under test. So it can't be mocked.
You need to delegate httpclient object creation to another method with belongs to a different class (something similar to HttpClientFactory class). That HttpClientFactory class should only be responsible for creating httpClient instances. If you need you can write separate unit test case for the class.
#Inject
private HttpClientFactory httpClientFactory;
public String doRequest(ClassicHttpRequest request) {
String result = null;
try (CloseableHttpClient httpclient = httpClientFactory.getInstance()) {
final HttpClientContext clientContext = HttpClientContext.create();
try (CloseableHttpResponse response = httpclient.execute(request, clientContext)) {
result = EntityUtils.toString(response.getEntity());
}
} catch (URISyntaxException e) {
log.error("Invalid URI {}", e);
} catch (IOException e) {
log.error("Failed to make HTTP Request {}", e);
} catch (ParseException e) {
log.error("Failed parsing response body {}", e);
}
return result;
}
Now you can mock response like below:
#Mock
private HttpClientFactory httpClientFactory;
public String testDoRequest(ClassicHttpRequest request) {
....
CloseableHttpClient mockedClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedResponse = mock(CloseableHttpResponse.class);
when(httpClientFactory.getInstance()).thenReturn(mockedClient);
when(mockedClient.execute(eq(request), any(clientContext))).thenReturn(mockedResponse);
....}

java.net.ConnectException when Request to nonstandard HTTP/HTTPS port

I want to access REST server to activate my software. For this, I provide an URL, request body, and some credentials. When I use port 3000 (URL http://localhost:3000/activation), I got java.net.ConnectException. But it be normal when use port 80 or 443.
FYI, my simulator already run and I can access it by Postman.
How can I access port 3000 or other port using HttpClient?
This is my code in Java 1.8
private static ResponseEntityCustom sendRequestHttp(String method, String url, Map<String, List<String>> parameters, Headers requestHeaders, String body, int timeout) throws HttpRequestException {
Builder builder = HttpRequest.newBuilder();
URI uri = null;
try {
uri = new URI(url);
} catch (URISyntaxException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
System.out.println("uri.getPort(); = "+uri.getPort());
builder.uri(uri);
builder.timeout(Duration.ofMillis(timeout));
System.out.println("URL : "+url);
System.out.println("Method : "+method);
if(method.equalsIgnoreCase(HttpMethod.POST) ||
method.equalsIgnoreCase(HttpMethod.PUT) ||
method.equalsIgnoreCase(HttpMethod.PATCH)
)
{
if(body == null && parameters != null)
{
body = Utility.buildQuery(parameters);
}
if(body != null)
{
builder.method(method, BodyPublishers.ofByteArray(body.getBytes()));
}
}
else if(method.equalsIgnoreCase(HttpMethod.DELETE))
{
builder.DELETE();
}
else if(method.equalsIgnoreCase(HttpMethod.GET))
{
builder.GET();
}
for(Map.Entry<String, List<String>> entry : requestHeaders.entrySet()) {
String key = entry.getKey();
for (String value : entry.getValue())
{
builder.header(key, value);
}
}
HttpRequest request = builder.build();
HttpClient client = HttpClient.newBuilder()
.version(Version.HTTP_1_1)
.followRedirects(Redirect.NORMAL)
.connectTimeout(Duration.ofMillis(timeout))
.build();
HttpResponse<String> response;
ResponseEntityCustom responseEntity = new ResponseEntityCustom();
try {
response = client.send(request, BodyHandlers.ofString());
System.out.println("response = "+response.toString());
responseEntity = new ResponseEntityCustom(response.body(), response.statusCode(), response.headers());
}
catch (InterruptedException e)
{
e.printStackTrace();
throw new HttpRequestException(e.getMessage());
}
catch(ConnectException e)
{
e.printStackTrace();
throw new HttpRequestException(e.getMessage());
}
catch(IOException e)
{
e.printStackTrace();
throw new HttpRequestException(e.getMessage());
}
return responseEntity;
}

Spring Boot: Manage Multiple Endpoint with Rest Template?

In my Spring Boot project I have set several endpoints (which refer to some GET or POST REST API) in the application-dev.yml file.
spring:
username: xxx
password: acb132
route:
source:
protocol: https://
ip: 10.xxx.y.zz/
root: "swdfr/"
paths: >
- "ofh/ert/hAFG5"
- "ofh/ert/ryt54"
I would like to manage these endpoints within a service class with a single method. Currently I have implemented this solution:
//REST CONTROLLER
#GetMapping("/Multiple_Get")
public void manageGetEndpointsWithRestTemplate() throws Exception{
final String methodName = "manageGetEndpointsWithRestTemplate()";
try {
service.manageGetEndpointsWithRestTemplate();
} catch (final Exception e) {
this.errorLog(methodName, e);
throw e;
}
}
//SERVICE
#ResponseBody
public void manageGetEndpointsWithRestTemplate() {
final String methodName = "manageGetEndpointsWithRestTemplate()";
try {
String urlGet1 = protocol + ip + root + paths.get(0);
String urlGet2 = protocol + ip + root + paths.get(1);
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth(username, password);
HttpEntity request = new HttpEntity(headers);
try {
RestTemplate restTemplate;
if (urlGet1.startsWith("https") || urlGet2.startsWith("https")) {
restTemplate = getRestTemplateForSelfSsl();
} else {
restTemplate = new RestTemplate();
}
// GET1
ResponseEntity<String> response1 = restTemplate.exchange(urlGet1, HttpMethod.GET, request,
String.class);
HttpStatus statusCode1 = response1.getStatusCode();
logger.info("STATUS GET1: " + statusCode1);
// GET2
ResponseEntity<String> response2 = restTemplate.exchange(urlGet2, HttpMethod.GET, request,
String.class);
HttpStatus statusCode2 = response2.getStatusCode();
logger.info("STATUS GET2: " + statusCode2);
} catch (HttpStatusCodeException e) {
logger.error(e.getMessage());
}
} catch (Exception e) {
logger.error(e.getMessage());
}
}
public RestTemplate getRestTemplateForSelfSsl()
throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
TrustStrategy acceptingTrustStrategy = (X509Certificate[] x509Certificates, String s) -> true;
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy)
.build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
return restTemplate;
}
I would like to use a single method making it as generalizable as possible especially if I have to manage a large number of endpoints.
Do you have any ideas?
Thanks in advance
You can iterate over all the paths and execute the common code, considering you are doing GET request for all.
#ResponseBody
public void manageGetEndpointsWithRestTemplate() {
final String methodName = "manageGetEndpointsWithRestTemplate()";
try {
paths.forEach(path -> {
String urlGet = protocol + ip + root + path;
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth(username, password);
HttpEntity request = new HttpEntity(headers);
try {
RestTemplate restTemplate = urlGet.startsWith("https") ? getRestTemplateForSelfSsl() : new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(urlGet, HttpMethod.GET, request,
String.class);
HttpStatus statusCode = response.getStatusCode();
logger.info("STATUS GET - {} : {}", urlGet, statusCode);
} catch (HttpStatusCodeException e) {
logger.error(e.getMessage());
}
});
} catch (Exception e) {
logger.error(e.getMessage());
}
}
In case you're gonna use POST or any other HTTP methods, mention in application-dev.yml file along with the paths. Add few extra logic to determine the HTTP.XXX before passing into restTemplate.exchange().

Java - Perform oAuth1.0 authenticated request with given consumerKey, consumerSecret, accessToken, accessTokenSecret and realm

I am trying to send an http post to a given oAuth1.0 protected endpoint, the owner of the endpoint provided to me:
consumerKey
consumerSecret
accessToken
accessTokenSecret
realm
I wrote some code based on How to call API (Oauth 1.0)?
public class HttpAuthPost {
public HttpAuthPost() {
realmID = "XXXXXXX";
String consumerKey = "kjahsdkjhaskdjhaskjdhkajshdkajsd";
String consumerSecret = "jklahsdkjhaskjdhakjsd";
String accessToken = "iuyhiuqhwednqkljnd";
String accessTokenSecret = "oihkhnasdiguqwd56qwd";
setupContext(consumerKey, consumerSecret, accessToken, accessTokenSecret);
}
public void setupContext(String consumerKey, String consumerSecret, String accessToken, String accessTokenSecret) {
this.oAuthConsumer = new CommonsHttpOAuthConsumer(consumerKey, consumerSecret);
oAuthConsumer.setTokenWithSecret(accessToken, accessTokenSecret);
oAuthConsumer.setSigningStrategy(new AuthorizationHeaderSigningStrategy());
}
public void authorize(HttpRequestBase httpRequest) throws FMSException {
try {
oAuthConsumer.sign(httpRequest);
} catch (OAuthMessageSignerException e) {
throw new FMSException(e);
} catch (OAuthExpectationFailedException e) {
throw new FMSException(e);
} catch (OAuthCommunicationException e) {
throw new FMSException(e);
}
}
public String executeGetRequest(String customURIString, String _content) throws UnsupportedEncodingException {
DefaultHttpClient client = new DefaultHttpClient();
HttpPost httpRequest = null;
//Preparing HttpEntity and populating httpRequest
try {
authorize(httpRequest);
} catch (FMSException e) {
e.printStackTrace();
}
HttpResponse httpResponse = null;
try {
HttpHost target = new HttpHost(uri.getHost(), -1, uri.getScheme());
httpResponse = client.execute(target, httpRequest);
// Process response and generate output
return output;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
I did some tests and I am getting this error: USER_ERROR : header is not NLAuth scheme.
I noticed the realm value is never actually set in the oAuthConsumer configuration, I try to find a way to specify the realm but I have not found a way to do it.
Does anyone have a clue on this?
Well the solution was actually pretty simple and now that I figured it out it seems obvious. Adding the realm as an additional parameter to the authconsumer worked for me.
Hope this help someone else in the future.
public void setupContext(String consumerKey, String consumerSecret, String accessToken, String accessTokenSecret) {
this.oAuthConsumer = new CommonsHttpOAuthConsumer(consumerKey, consumerSecret);
oAuthConsumer.setTokenWithSecret(accessToken, accessTokenSecret);
oAuthConsumer.setSigningStrategy(new AuthorizationHeaderSigningStrategy());
HttpParameters parameters = new HttpParameters();
parameters.put("realm", realmID);
oAuthConsumer.setAdditionalParameters(parameters);
}

Getting SSLException while payment gateway integration

I am just integrating Frimi payment gateway .In first step am trying to generate access tokes ,and after generating tokes payment link will be called .
But am getting error :- SSLException: Connection closed by peer.
public void TokenGenerationProcess() {
class WaitingforResponse extends AsyncTask<Void, Void, String> {
ProgressDialog progressDialog;
#Override
protected void onPreExecute() {
super.onPreExecute();
// progressDialog = ProgressDialog.show(getActivity().getBaseContext(), "Waiting for Token Generation...", "Please Wait...", false, false);
}
#Override
protected String doInBackground(Void... params) {
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.build();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
String json = "";
RequestBody body = RequestBody.create(mediaType, json);
/* https://ideabiz?grant_type=password&username=99R_User&password=99R_User&scope=SANDBOX
https://igrant_type=refresh_token&refresh_token="+Message+"&scope=SANDBOX */
// Message="d6a5be5f5455c87c21442ba3d5201c8c";
Request request = new Request.Builder()
.url("https://uatapi.nationstrust.com:8243/token?grant_type=client_credentials")
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", "Basic N000SDNmU3RtVERuZmZ1R0JNMlBGR1FXdmtFYTpEQUFJcEprVUhjdXBwcEx4dkRPSkFYZjNwMmth")
.post(body)
.build();
//Bearer aUFVMWFRd2Vpb2txQUI2VTFkajBFZWRkTTBZYTpEMzZfbU9GM2ZlYkFEaWRiMGp0ZUZCN2xGUUlh")
try {
okhttp3.Response response = client.newCall(request).execute();
String test = response.body().string();
if (response.isSuccessful()) {
/* int success = response.code();
Headers responseHeaders = response.headers();
for (int i = 0; i < responseHeaders.size(); i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
System.out.println(test);
/// {"scope":"default","token_type":"bearer","expires_in":3600,"refresh_token":"2d23c431f25e10e5abcd16bea931d0a","access_token":"b0bffddb2be384c53135cadf0f565c2"}
try {
JSONObject jsonObject = new JSONObject(test);
Log.e(" scope", "" + jsonObject.get("scope"));
Log.e(" token_type", "" + jsonObject.get("token_type"));
Log.e(" expires_in", "" + jsonObject.get("expires_in"));
Log.e(" refresh_token", "" + jsonObject.get("refresh_token"));
Log.e(" access_token", "" + jsonObject.get("access_token"));
// Message = (String) jsonObject.get("refresh_token");
String access_token = (String) jsonObject.get("access_token");
String refresh_token = (String) jsonObject.get("refresh_token");
PersistenceManager.saveRefreshToken(getApplicationContext(), refresh_token);
PersistenceManager.saveSessionId(getApplicationContext(), access_token);
PersistenceManager.saveTime(getApplicationContext(), String.valueOf(System.currentTimeMillis()));
*/ // reference= (String) jsonObject.get("Refno");
} else {
System.out.println(test);
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
// progressDialog.dismiss();
}
}
WaitingforResponse WaitingforResponse = new WaitingforResponse();
WaitingforResponse.execute();
}
See Stacktrace below:
// here I have posted full code what I am trying to generate access tokes.
/please help..help would be greatly appreciated
//Please use this it will work
OkHttpClient client = null;
try {
client = new OkHttpClient.Builder()
.connectTimeout(120, TimeUnit.SECONDS)
.writeTimeout(120, TimeUnit.SECONDS)
.readTimeout(120, TimeUnit.SECONDS)
.sslSocketFactory(new Tls12SocketFactory(), provideX509TrustManager())
.build();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
//add this method
public X509TrustManager provideX509TrustManager() {
try {
TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
factory.init((KeyStore) null);
TrustManager[] trustManagers = factory.getTrustManagers();
return (X509TrustManager) trustManagers[0];
} catch (NoSuchAlgorithmException | KeyStoreException exception) {
Log.e(getClass().getSimpleName(), "not trust manager available", exception);
}
return null;
}
//create another java class
public class Tls12SocketFactory extends SSLSocketFactory {
private SSLSocketFactory internalSSLSocketFactory;
public Tls12SocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);
internalSSLSocketFactory = context.getSocketFactory();
}
#Override
public String[] getDefaultCipherSuites() {
return internalSSLSocketFactory.getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites() {
return internalSSLSocketFactory.getSupportedCipherSuites();
}
#Override
public Socket createSocket() throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket());
}
#Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose));
}
#Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
#Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort));
}
#Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
#Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
}
private Socket enableTLSOnSocket(Socket socket) {
if(socket != null && (socket instanceof SSLSocket)) {
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
}
return socket;
}
}
Your are not building the call the right way, this is how you should do it.
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.build();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody body = RequestBody.create(mediaType, "grant_type=client_credentials");
Request request = new Request.Builder()
.url("https://uatapi.nationstrust.com:8243/token")
.post(body)
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", "Basic N000SDNmU3RtVERuZmZ1R0JNMlBGR1FXdmtFYTpEQUFJcEprVUhjdXBwcEx4dkRPSkFYZjNwMmth")
.build();
try {
Response response = client.newCall(request).execute();
String test = response.body().string();
if (response.isSuccessful()) {
System.out.println(test);
} else {
System.out.println(response.code() +" : "+ response.message());
}
} catch (Exception e) {
e.printStackTrace();
}
Your JSON response will look like this:
{
"access_token": "7389a302-82f9-3fa9-b7a3-fefa45d2de32",
"scope": "am_application_scope default",
"token_type": "Bearer",
"expires_in": 500
}
Edit:
This however does not explain why you are getting the ssl exception. I would need more information to figure that out. What Android OS version is the device are you running your app on. If you are WiFi consider using your mobile network instead.
Further Edit:
I see you running version 4.4.2 try run a device with OS version 5.1 upwards. You'll find the problem might go away. If that doesn't make a difference try add this code on your code, make sure it executes before the call is made.
public static void initializeSSLContext(Context mContext) {
try {
SSLContext.getInstance("TLSv1.2");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
try {
ProviderInstaller.installIfNeeded(mContext.getApplicationContext());
} catch (GooglePlayServicesRepairableException e) {
e.printStackTrace();
} catch (GooglePlayServicesNotAvailableException e) {
e.printStackTrace();
}
}
If that doesn't help, you clearly have a problem with your server's ssl certificates. Try get your server admin to assist.

Categories