Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Testing HTTPS Web Services With Cucumber

DZone's Guide to

Testing HTTPS Web Services With Cucumber

Let's make testing HTTPS web services easier with a Java REST client and Cucumber, including tips on configuring and customizing your tests.

· Java Zone ·
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

In this article, I am writing a small example on how to test HTTPS web services using a Java REST client in a Cucumber test.

Any Java developer can have to test web services on DEV, PRE-PRODUCTION, or PRODUCTION, and in order to avoid regression, BDD methodology is appreciated.

In this context, many feel quite good with Cucumber API.

In the example below, I'll use an HTTPS Java REST client that ignores certification in order to facilitate switching between environments and avoid "playing" with KeyStore files anywhere.

Let's begin with the Maven dependencies.

Cucumber dependency with test scope:

<dependency>
    <groupId>info.cukes</groupId>
    <artifactId>cucumber-java</artifactId>
    <version>1.2.5</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>info.cukes</groupId>
    <artifactId>cucumber-junit</artifactId>
    <version>1.2.5</version>
    <scope>test</scope>
</dependency>


Maven's HTTP client dependency:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.3</version>
</dependency>


We'll need our famous HTTPS Java REST client ignoring HTTPS constraints. Note that you can also add a certificate exception to your local JVM.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.sun.org.apache.xml.internal.security.utils.Base64;

import com.ahajri.cucumber.rest.beans.WsResponse;

/**
 * ERST Client ignoring HTTPS certificate
 * 
 * @author ahajri
 *
 */
public class HttpsClient {
    private String username;
    private String password;

    public HttpsClient(String username, String password) {
        super();
        this.username = username;
        this.password = password;
    }

    /**
     * 
     * @param httpsUrl
     * @return {@link WsResponse}
     * @throws Exception
     */
    public WsResponse doGet(String httpsUrl) throws Exception {
        HttpsURLConnection connection = initHttpsConnection(httpsUrl, username, password);
        BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));


        int httpStatus = 500;
        Map < String, Object > jsonMap = new HashMap < > ();
        Type mapType = new TypeToken < Map < String, Object >> () {}.getType();
        String json;
        while ((json = br.readLine()) != null) {
            jsonMap = new Gson().fromJson(json, mapType);
            System.out.println(json);
            httpStatus = Integer.parseInt((String) jsonMap.get("status"));
        }
        br.close();
        connection.disconnect();
        return new WsResponse(httpStatus, jsonMap);
    }

    /**
     * 
     * @param https_url
     * @param username
     * @param password
     * @return {@link HttpsURLConnection}
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     * @throws IOException
     */
    private HttpsURLConnection initHttpsConnection(String https_url, String username, String password)
    throws NoSuchAlgorithmException, KeyManagementException, IOException {

        URL url = new URL(https_url);
        // Create a context that doesn't check certificates.
        SSLContext ssl_ctx = SSLContext.getInstance("TLS");
        TrustManager[] trust_mgr = getTrustMgr();
        ssl_ctx.init(null, // key manager
            trust_mgr, // trust manager
            new SecureRandom()); // random number generator
        HttpsURLConnection.setDefaultSSLSocketFactory(ssl_ctx.getSocketFactory());

        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
        String userpass = username + ":" + password;
        String basicAuth = "Basic " + new String(Base64.encode(userpass.getBytes()));
        connection.setRequestProperty("Authorization", basicAuth);
        // Guard against "bad hostname" errors during handshake.
        connection.setHostnameVerifier(new HostnameVerifier() {
            public boolean verify(String host, SSLSession sess) {
                if (host.equals("localhost"))
                    return true;
                else
                    return false;
            }
        });

        return connection;

    }

    private void printHttpsCert(HttpsURLConnection connection) {
        if (connection != null) {

            try {

                System.out.println("Response Code : " + connection.getResponseCode());
                System.out.println("Cipher Suite : " + connection.getCipherSuite());
                System.out.println("\n");

                Certificate[] certs = connection.getServerCertificates();
                for (Certificate cert: certs) {
                    System.out.println("Cert Type : " + cert.getType());
                    System.out.println("Cert Hash Code : " + cert.hashCode());
                    System.out.println("Cert Public Key Algorithm : " + cert.getPublicKey().getAlgorithm());
                    System.out.println("Cert Public Key Format : " + cert.getPublicKey().getFormat());
                    System.out.println("\n");
                }

            } catch (SSLPeerUnverifiedException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private TrustManager[] getTrustMgr() {
        TrustManager[] certs = new TrustManager[] {
            new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }

                public void checkClientTrusted(X509Certificate[] certs, String t) {}

                public void checkServerTrusted(X509Certificate[] certs, String t) {}
            }
        };
        return certs;
    }
}


WsResponse is a bean class that has the HTTP code and the mapped returned data. You can choose whatever you need:

import java.io.Serializable;

import java.util.Map;

/**
 * 
 * @author ahajri
 *
 */
public class WsResponse implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = -4929529852038654613 L;

    private int httpStatus;
    private Map < String, Object > jsonMap;

    public WsResponse(int httpStatus) {
        super();
        this.httpStatus = httpStatus;
    }

    public WsResponse(int httpStatus, Map < String, Object > jsonMap) {
        super();
        this.httpStatus = httpStatus;
        this.jsonMap = jsonMap;
    }

    public int getHttpStatus() {
        return httpStatus;
    }

    public void setHttpStatus(int httpStatus) {
        this.httpStatus = httpStatus;
    }

    public Map < String, Object > getJsonMap() {
        return jsonMap;
    }

    public void setJsonMap(Map < String, Object > jsonMap) {
        this.jsonMap = jsonMap;
    }

    @Override
    public String toString() {
        return "WsResponse [httpStatus=" + httpStatus + ", jsonMap=" + jsonMap.toString() + "]";
    }

}


Now let's configure our Cucumber feature. If you are not familiar with Cucumber, please have a look here. The feature file will look like this:

@call_preprod
Feature: xWebService - test PreProd Environment

  Scenario: get some result
    Given the following URL, Login, Password  "https://someurl:25152/ws/api/v1/sometask/2017/2187AA36" AND "ws_user" AND "07cdec654efe"
    When call https ws
    Then found result


@call_preprod is the feature tag. This could help us when doing the same test in a different environment. For example, you can change the tag to ping the good URL. You can also see that I added the WS username/password in the @Given step, and you can change it for every feature file or scenario.

The test class looks like this:

import org.junit.runner.RunWith;

import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;

/**
 * 
 * @author ahajri
 *
 */
@RunWith(Cucumber.class)
@CucumberOptions(features = { "classpath:feature/preprod.feature" }, strict = false, tags = { "@call_preprod" })
public class RunWsPreProdTest {

}


We defined features that define the local path to *.feature files. We can add more and prompt the tag for every test. The step definition class looks like: 

import org.junit.Assert;

import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
import com.ahajri.cucumber.rest.beans.WsResponse;
import com.ahajri.cucumber.rest.utils.HttpsClient;

/**
 * 
 * @author ahajri
 */
public class PreProdStepsDef {

    private String someUrl;

    private HttpsClient httpsClient;

    private WsResponse getResult;

    @Given("^the following URL, Login, Password  \"(.*?)\" AND \"(.*?)\" AND \"(.*?)\"$")
    public void the_following_URL_Login_Password(String url, String username, String password)
    throws Throwable {
        httpsClient = new HttpsClient(username, password);
        someUrl = url;
    }

    @When("^call https ws$")
    public void call_https_ws() throws Throwable {
        getResult = httpsClient.doGet(someUrl);

    }

    @Then("^found result$")
    public void found_result() throws Throwable {
        Assert.assertNotNull(getResult);
        Assert.assertEquals(200, getResult.getHttpStatus());
    }
}


Hope this could be helpful!

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat

Topics:
cucumber ,rest ,java ,web service ,https ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}