How to make a trust store to be part of Java API test Maven project

In my previous post we were talking about general concept of testing HTTPs-based API in Java. We figured out how to create custom trust store that would contain the certificate that our server provides to prove its identity so that our code won’t fail with "PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target" error message. Now we’ll look at a more formed solution that would include using Maven and JUnit5. The key problem we’re going to solve today is how to make created trust store a prat of our test project so that our tests would use the same store wherever they are executed.

Example description: Simple API-testing Maven project intended to work with HTTPs

We’ll take a look at the example that is very similar to the one we looked into at my previous post. But this time we’re going to have a well-styled test project that would use Maven and JUnit instead of just running everything in main method. So our pom.xml would be the following:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>click.webelement</groupId>
    <artifactId>ssl-api-tests</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.5.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

And here is what our test class look like

package click.webelement.https;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;

public class ApiWithSSLTest {

    private final static int OK_CODE = 200;

    @Test
    public void callHttpsEndpoint() throws IOException {
        HttpURLConnection connection = (HttpURLConnection) new URL("https://webelement.click").openConnection();
        Assertions.assertEquals(OK_CODE, connection.getResponseCode(), "Response code should be successful");
    }

}

Since we’re going concentrate on how to add a trust store to your project rather than how to prepare a trust store itself, I assume that you have already it prepared. If not, please visit my previous post where I show how to do that.

Add trust store to your Maven project

First thing that you need to do is to place trust store file to resources folder of your Maven Java project (since we’re talking about testing, and JUnit scope is test, we keep all the code and resources in src/test folder). Then you need to get the path to that store which might be different on different machines. There is the way however to obtain current path of any resource using the following approach:

Get URI of a resource:

URI trustStoreURI = ApiWithSSLTest.class.getClassLoader().getResource(TRUST_STORE_FILE).toURI();

Translate URI to a file path

String trustStoreFilePath = new File(trustStoreURI).getAbsolutePath();

So having all these points combined we end up with the following test class:

package click.webelement.https;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;

public class ApiWithSSLTest {

    private final static String TRUST_STORE_FILE = "mytruststore";
    private final static String TRUST_STORE_PASSWORD = "mystorepass";

    private final static int OK_CODE = 200;

    @BeforeAll
    public static void setUpAll() throws URISyntaxException {
        URI trustStoreURI = ApiWithSSLTest.class.getClassLoader().getResource(TRUST_STORE_FILE).toURI();
        System.setProperty("javax.net.ssl.trustStore", new File(trustStoreURI).getAbsolutePath());
        System.setProperty("javax.net.ssl.trustStorePassword", TRUST_STORE_PASSWORD);
    }

    @Test
    public void callHttpsEndpoint() throws IOException {
        HttpURLConnection connection = (HttpURLConnection) new URL("https://webelement.click").openConnection();
        Assertions.assertEquals(OK_CODE, connection.getResponseCode(), "Response code should be successful");
    }

}

Now we have bound our code to the truststore that is a part of the project. Hence being a part of CI process, your code will get complied with your store and will be using one wherever it is executed.

If you still have the questions please send them to me using this form. I will amend the article according to your feedback.