As part of testing our Amazon API Gateway deployment, we set up JUnit tests to run automated Swagger/OpenAPI validation using Swagger Request Validator and REST-assured. This allows us to write simple tests in a fluent style, with automatic validation that requests and responses match the Swagger API specification deployed to API Gateway.
given()
.log().all()
.filter(validationFilter)
.when()
.post("/oauth2/token")
.then()
.assertThat()
.statusCode(200);
Out of the box, REST-assured does not work with Amazon’s API Gateway endpoints using Java 8. Attempting to use it results in a handshake error when connecting to the API Gateway endpoint.
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
To workaround this problem, you need to enable Server Name Indication (SNI) for SSL when creating your SSL sockets, and configure the REST-assured client to use those created sockets.
SSL socket creation is handled by the
org.apache.http.conn.ssl.SSLSocketFactory
class. We need to extend this
class and implement the createSocket
method with SNI enabled for API
Gateway. API Gateway uses TLSv1.2 encryption, and our socket should be
enabled with that encryption protocol.
public class GatewaySslSocketFactory extends SSLSocketFactory {
GatewaySslSocketFactory(SSLContext sslContext, X509HostnameVerifier hostnameVerifier) {
super(sslContext, hostnameVerifier);
}
@Override
public Socket createSocket(HttpParams params) throws IOException {
SSLSocket sslSocket = (SSLSocket) super.createSocket(params);
// Set the encryption protocol
sslSocket.setEnabledProtocols(new String[]{"TLSv1.2"});
// Configure SNI
SNIHostName serverName = new SNIHostName("4********r.execute-api.us-east-1.amazonaws.com");
SSLParameters sslParams = sslSocket.getSSLParameters();
sslParams.setServerNames(Collections.singletonList(serverName));
sslSocket.setSSLParameters(sslParams);
return sslSocket;
}
}
With our SocketFactory
ready, we configure REST-assured to use it. In
this example, we configure it as part of a base class for unit tests. This
base class creates an instance of the SwaggerValidationFilter
for
automatic verification of your spec, and configures REST-assured to user
our socket factory for all connections.
public class BaseApiGatewayTest {
private static final String SWAGGER_JSON_URL = "/public-swagger.json";
public static final String API_GATEWAY_HOST_URL = "https://4********r.execute-api.us-east-1.amazonaws.com";
private static final int API_GATEWAY_HOST_PORT = 443;
protected final SwaggerValidationFilter validationFilter = new SwaggerValidationFilter(SWAGGER_JSON_URL);
@Before
public void setUpBaseApiGateway() throws NoSuchAlgorithmException {
// Set the host, port, and base path
RestAssured.baseURI = API_GATEWAY_HOST_URL;
RestAssured.port = API_GATEWAY_HOST_PORT;
RestAssured.basePath = "dev";
// Use our custom socket factory
SSLSocketFactory customSslFactory = new GatewaySslSocketFactory(
SSLContext.getDefault(), SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
RestAssured.config = RestAssured.config().sslConfig(
SSLConfig.sslConfig().sslSocketFactory(customSslFactory));
RestAssured.config.getHttpClientConfig().reuseHttpClientInstance();
}
}
With these pieces in place, writing API Gateway tests is fairly straight forward. Simply extend the base test class and start verifying expected behaviour. The Swagger validation filter will check for conformance to the spec; you will need to check for appropriate response codes and business logic.
public class OAuth2TokenTest extends BaseApiGatewayTest {
@Test
public void testGetToken() {
given()
.filter(validationFilter)
.when()
.post("/oauth2/token")
.then()
.assertThat()
.statusCode(200);
}
}