Repository: eclipse/microprofile-open-api
Pull request #146 (2017-12-11 20:38:10)

Tck endpoint tests
PR for #69


Patch:
diff --git a/tck/pom.xml b/tck/pom.xml
index d63ccdc..ae23ba9 100644
--- a/tck/pom.xml
+++ b/tck/pom.xml
@@ -88,15 +88,15 @@
         </dependency>
 
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <version>4.12</version>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <version>6.9.9</version>
             <scope>compile</scope>
         </dependency>
 
         <dependency>
-            <groupId>org.jboss.arquillian.junit</groupId>
-            <artifactId>arquillian-junit-container</artifactId>
+            <groupId>org.jboss.arquillian.testng</groupId>
+            <artifactId>arquillian-testng-container</artifactId>
             <scope>compile</scope>
         </dependency>
 


Test patch:
diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/EndpointTest.java b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/AirlinesAppTest.java
similarity index 54%
rename from tck/src/main/java/org/eclipse/microprofile/openapi/tck/EndpointTest.java
rename to tck/src/main/java/org/eclipse/microprofile/openapi/tck/AirlinesAppTest.java
index 7018451..15a492c 100644
--- a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/EndpointTest.java
+++ b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/AirlinesAppTest.java
@@ -16,14 +16,12 @@
 
 package org.eclipse.microprofile.openapi.tck;
 
-import static io.restassured.RestAssured.given;
 import static org.hamcrest.Matchers.both;
 import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.either;
-import static org.hamcrest.Matchers.empty;
 import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.greaterThan;
 import static org.hamcrest.Matchers.hasItem;
 import static org.hamcrest.Matchers.hasItems;
 import static org.hamcrest.Matchers.hasKey;
@@ -32,99 +30,18 @@ import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.notNullValue;
 import static org.hamcrest.collection.IsMapWithSize.aMapWithSize;
 
-import java.io.File;
-import java.net.MalformedURLException;
-import java.net.URL;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
-import org.apache.commons.lang3.StringUtils;
-import org.eclipse.microprofile.openapi.tck.utils.YamlToJsonConverterServlet;
 import org.jboss.arquillian.container.test.api.Deployment;
 import org.jboss.arquillian.container.test.api.RunAsClient;
-import org.jboss.arquillian.junit.Arquillian;
 import org.jboss.shrinkwrap.api.ShrinkWrap;
 import org.jboss.shrinkwrap.api.spec.WebArchive;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import org.testng.annotations.Test;
 
-import io.restassured.RestAssured;
-import io.restassured.parsing.Parser;
 import io.restassured.response.ValidatableResponse;
 
-@RunWith(Arquillian.class)
-public class EndpointTest {
-    private static final String APPLICATION_JSON = "application/json";
-    private static final String APPLICATION_YAML = "application/yaml";
-
-    private static final String DEFAULT_PROTOCOL = "http";
-    private static final String DEFAULT_HOST = "localhost";
-    private static final int DEFAULT_PORT = 9080;
-
-    private static ValidatableResponse vr;
-    private static String serverUrl;
-    private static String username;
-    private static String password;
-    private static Map<String, String> headers = new HashMap<>();
-
-    @BeforeClass
-    public static void setUp() throws MalformedURLException {
-        // set base URI and port number to use for all requests
-        serverUrl = System.getProperty("test.url");
-        String protocol = DEFAULT_PROTOCOL;
-        String host = DEFAULT_HOST;
-        int port = DEFAULT_PORT;
-
-        if (serverUrl != null) {
-            URL url = new URL(serverUrl);
-            protocol = url.getProtocol();
-            host = url.getHost();
-            port = (url.getPort() == -1) ? DEFAULT_PORT : url.getPort();
-        }
-
-        RestAssured.baseURI = protocol + "://" + host;
-        RestAssured.port = port;
-
-        username = System.getProperty("test.user");
-        password = System.getProperty("test.pwd");
-
-        if (username != null && password != null) {
-            RestAssured.authentication = RestAssured.basic(username, password);
-            RestAssured.useRelaxedHTTPSValidation();
-        }
-        RestAssured.defaultParser = Parser.JSON;
-        
-        if (StringUtils.isBlank(serverUrl)) {
-            serverUrl = DEFAULT_PROTOCOL + "://" + DEFAULT_HOST + ":" + DEFAULT_PORT;
-        }
-        headers.put(YamlToJsonConverterServlet.TCK_HEADER_SERVERURL, serverUrl);
-        if (StringUtils.isNotBlank(username)) {
-            headers.put(YamlToJsonConverterServlet.TCK_HEADER_USERNAME, username);
-        }
-        if (StringUtils.isNotBlank(password)) {
-            headers.put(YamlToJsonConverterServlet.TCK_HEADER_PASSWORD, password);
-        }
-    }
-
-    @Deployment(name = "proxy")
-    public static WebArchive createProxy() {
-        return ShrinkWrap.create(WebArchive.class, "proxy.war")
-                .addClass(YamlToJsonConverterServlet.class)
-                .addAsLibraries(new File("./lib/httpclient-4.5.2.jar"))
-                .addAsLibraries(new File("./lib/httpcore-4.4.4.jar"))
-                .addAsLibraries(new File("./lib/jackson-core-2.9.2.jar"))
-                .addAsLibraries(new File("./lib/jackson-dataformat-yaml-2.9.2.jar"))
-                .addAsLibraries(new File("./lib/jackson-databind-2.9.2.jar"))
-                .addAsLibraries(new File("./lib/jackson-annotations-2.9.1.jar"))
-                .addAsLibraries(new File("./lib/snakeyaml-1.18.jar"))
-                .addAsLibraries(new File("./lib/commons-logging-1.2.jar"))
-                .addAsLibraries(new File("./lib/commons-lang3-3.4.jar"));
-    }
-
+public class AirlinesAppTest extends AppTestBase {
     @Deployment(name = "airlines")
     public static WebArchive createDeployment() {
         return ShrinkWrap.create(WebArchive.class, "airlines.war")
@@ -132,50 +49,51 @@ public class EndpointTest {
                 .addAsManifestResource("openapi.yaml", "openapi/openapi.yaml");
     }
 
-    @Before
-    public void setUpTest() {
-        vr = given().headers(headers).when().get("/proxy").then().statusCode(200);
-    }
-
-    @Test
     @RunAsClient
-    public void testVersion() {
+    @Test(dataProvider = "formatProvider")
+    public void testVersion(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         vr.body("openapi", equalTo("3.0.0"));
     }
 
-    @Test
     @RunAsClient
-    public void testInfo() {
+    @Test(dataProvider = "formatProvider")
+    public void testInfo(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         vr.body("info.title", equalTo("AirlinesRatingApp API"));
         vr.body("info.version", equalTo("1.0"));
         vr.body("info.termsOfService", equalTo("http://airlinesratingapp.com/terms"));
     }
 
-    @Test
     @RunAsClient
-    public void testContact() {
+    @Test(dataProvider = "formatProvider")
+    public void testContact(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         vr.body("info.contact.name", equalTo("AirlinesRatingApp API Support"));
         vr.body("info.contact.url", equalTo("http://exampleurl.com/contact"));
         vr.body("info.contact.email", equalTo("techsupport@airlinesratingapp.com"));
     }
 
-    @Test
     @RunAsClient
-    public void testLicense() {
+    @Test(dataProvider = "formatProvider")
+    public void testLicense(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         vr.body("info.license.name", equalTo("Apache 2.0"));
         vr.body("info.license.url", equalTo("http://www.apache.org/licenses/LICENSE-2.0.html"));
     }
 
-    @Test
     @RunAsClient
-    public void testExternalDocumentation() {
+    @Test(dataProvider = "formatProvider")
+    public void testExternalDocumentation(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         vr.body("externalDocs.description", equalTo("instructions for how to deploy this app"));
         vr.body("externalDocs.url", containsString("README.md"));
     }
 
-    @Test
     @RunAsClient
-    public void testServer() {
+    @Test(dataProvider = "formatProvider")
+    public void testServer(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         vr.body("servers", hasSize(2));
         vr.body("servers.url", hasSize(2));
 
@@ -209,15 +127,14 @@ public class EndpointTest {
         url = "http://gigantic-server.com:80";
         serverPath = "paths.'/reviews/{id}'.delete.servers.find { it.url == '" + url + "' }";
         vr.body(serverPath + ".description", equalTo("Unsecure server"));
-        vr.body(serverPath + ".variables", either(not(empty())).or(notNullValue()));
+        vr.body(serverPath + ".variables", not(hasSize(greaterThan(0))));
 
         url = "{protocol}://test-server.com";
         serverPath = "paths.'/reviews/{id}'.delete.servers.find { it.url == '" + url + "' }";
         vr.body(serverPath + ".description", equalTo("The production API server"));
         vr.body(serverPath + ".variables", aMapWithSize(1));
-        vr.body(serverPath + ".variables.proxyPath.description", equalTo("Base path of the proxy"));
-        vr.body(serverPath + ".variables.proxyPath.default", equalTo("http"));
-        vr.body(serverPath + ".variables.proxyPath.enum", containsInAnyOrder("http", "https"));
+        vr.body(serverPath + ".variables.protocol.default", equalTo("https"));
+        vr.body(serverPath + ".variables.protocol.enum", containsInAnyOrder("http", "https"));
 
         // Testing two @Server defined in an @operation annotation on a method
         // org.eclipse.microprofile.openapi.apps.airlines.resources.ReviewResource.createReview(Review)
@@ -238,34 +155,37 @@ public class EndpointTest {
         vr.body("paths.'/bookings'.get.servers", hasSize(2));
         vr.body("paths.'/bookings'.get.servers.url", hasSize(2));
 
-        url = "https://gigantic-server.com:80";
+        url = "http://gigantic-server.com:80";
         serverPath = "paths.'/bookings'.get.servers.find { it.url == '" + url + "' }";
-        vr.body(serverPath + ".description", equalTo("Secure server"));
-        vr.body(serverPath + ".variables", either(not(empty())).or(notNullValue()));
+        vr.body(serverPath + ".description", equalTo("Unsecure server"));
+        vr.body(serverPath + ".variables", not(hasSize(greaterThan(0))));
 
         url = "https://gigantic-server.com:443";
         serverPath = "paths.'/bookings'.get.servers.find { it.url == '" + url + "' }";
-        vr.body(serverPath + ".description", equalTo("Unsecure server"));
-        vr.body(serverPath + ".variables", either(not(empty())).or(notNullValue()));
+        vr.body(serverPath + ".description", equalTo("Secure server"));
+        vr.body(serverPath + ".variables", not(hasSize(greaterThan(0))));
     }
 
-    @Test
     @RunAsClient
-    public void testOperationAirlinesResource() {
+    @Test(dataProvider = "formatProvider")
+    public void testOperationAirlinesResource(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         vr.body("paths.'/'.get.summary", equalTo("Retrieve all available airlines"));
         vr.body("paths.'/'.get.operationId", equalTo("getAirlines"));
     }
 
-    @Test
     @RunAsClient
-    public void testOperationAvailabilityResource() {
+    @Test(dataProvider = "formatProvider")
+    public void testOperationAvailabilityResource(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         vr.body("paths.'/availability'.get.summary", equalTo("Retrieve all available flights"));
         vr.body("paths.'/availability'.get.operationId", equalTo("getFlights"));
     }
 
-    @Test
     @RunAsClient
-    public void testOperationBookingResource() {
+    @Test(dataProvider = "formatProvider")
+    public void testOperationBookingResource(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         vr.body("paths.'/bookings'.get.summary", equalTo("Retrieve all bookings for current user"));
         vr.body("paths.'/bookings'.get.operationId", equalTo("getAllBookings"));
 
@@ -283,9 +203,10 @@ public class EndpointTest {
         vr.body("paths.'/bookings/{id}'.delete.operationId", equalTo("deleteBookingById"));
     }
 
-    @Test
     @RunAsClient
-    public void testOperationReviewResource() {
+    @Test(dataProvider = "formatProvider")
+    public void testOperationReviewResource(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         vr.body("paths.'/reviews'.get.summary", equalTo("get all the reviews"));
         vr.body("paths.'/reviews'.get.operationId", equalTo("getAllReviews"));
 
@@ -308,9 +229,10 @@ public class EndpointTest {
         vr.body("paths.'/reviews/{user}/{airlines}'.get.operationId", equalTo("getReviewByAirlineAndUser"));
     }
 
-    @Test
     @RunAsClient
-    public void testOperationUserResource() {
+    @Test(dataProvider = "formatProvider")
+    public void testOperationUserResource(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         vr.body("paths.'/user'.post.summary", equalTo("Create user"));
         vr.body("paths.'/user'.post.description", equalTo("This can only be done by the logged in user."));
         vr.body("paths.'/user'.post.operationId", equalTo("createUser"));
@@ -342,16 +264,54 @@ public class EndpointTest {
         vr.body("paths.'/user/logout'.get.operationId", equalTo("logOutUser"));
     }
 
-    @Test
     @RunAsClient
-    public void testParameter() throws InterruptedException {
+    @Test(dataProvider = "formatProvider")
+    public void testAPIResponse(String type) {
+        ValidatableResponse vr = callEndpoint(type);
+        // @APIResponse at method level
+        vr.body("paths.'/availability'.get.responses", aMapWithSize(2));
+        vr.body("paths.'/availability'.get.responses.'200'.description", equalTo("successful operation"));
+        vr.body("paths.'/availability'.get.responses.'404'.description", equalTo("No available flights found"));
+
+        vr.body("paths.'/bookings'.post.responses", aMapWithSize(1));
+        vr.body("paths.'/bookings'.post.responses.'201'.description", equalTo("Booking created"));
+
+        vr.body("paths.'/user/{username}'.delete.responses", aMapWithSize(3));
+        vr.body("paths.'/user/{username}'.delete.responses.'200'.description", equalTo("User deleted successfully"));
+        vr.body("paths.'/user/{username}'.delete.responses.'400'.description", equalTo("Invalid username supplied"));
+        vr.body("paths.'/user/{username}'.delete.responses.'404'.description", equalTo("User not found"));
+    }
+
+    @RunAsClient
+    @Test(dataProvider = "formatProvider")
+    public void testAPIResponses(String type) {
+        ValidatableResponse vr = callEndpoint(type);
+        // @APIResponse annotations nested within @APIResponses
+        vr.body("paths.'/bookings/{id}'.get.responses", aMapWithSize(2));
+        vr.body("paths.'/bookings/{id}'.get.responses.'200'.description", equalTo("Booking retrieved"));
+        vr.body("paths.'/bookings/{id}'.get.responses.'404'.description", equalTo("Booking not found"));
+
+        vr.body("paths.'/reviews/{user}'.get.responses", aMapWithSize(2));
+        vr.body("paths.'/reviews/{user}'.get.responses.'200'.description", equalTo("Review(s) retrieved"));
+        vr.body("paths.'/reviews/{user}'.get.responses.'404'.description", equalTo("Review(s) not found"));
+
+        vr.body("paths.'/user/{username}'.put.responses", aMapWithSize(3));
+        vr.body("paths.'/user/{username}'.put.responses.'200'.description", equalTo("User updated successfully"));
+        vr.body("paths.'/user/{username}'.put.responses.'400'.description", equalTo("Invalid user supplied"));
+        vr.body("paths.'/user/{username}'.put.responses.'404'.description", equalTo("User not found"));
+    }
+
+    @RunAsClient
+    @Test(dataProvider = "formatProvider")
+    public void testParameter(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         testAvailabilityGetParamater(vr);
         testBookingIdMethods(vr);
         testReviewIdMethods(vr);
         testUserLoginMethods(vr);
     }
 
-    private void testUserLoginMethods(ValidatableResponse vr2) {
+    private void testUserLoginMethods(ValidatableResponse vr) {
         String reviewParameters = "paths.'/user/login'.get.parameters";
         vr.body(reviewParameters, hasSize(2));
         vr.body(reviewParameters + ".findAll { it }.name", hasItems("username", "password"));
@@ -370,7 +330,7 @@ public class EndpointTest {
         }
     }
 
-    private void testReviewIdMethods(ValidatableResponse vr2) {
+    private void testReviewIdMethods(ValidatableResponse vr) {
         String reviewParameters = "paths.'/reviews/{id}'.get.parameters";
         vr.body(reviewParameters, hasSize(1));
         vr.body(reviewParameters + ".findAll { it }.name", contains("id"));
@@ -425,66 +385,89 @@ public class EndpointTest {
         vr.body(availabilityParameters + ".findAll { it.name == 'numberOfChildren' }.schema.minimum", both(hasSize(1)).and(contains(0)));
     }
 
-    @Test
     @RunAsClient
-    public void testExplode() throws InterruptedException {
+    @Test(dataProvider = "formatProvider")
+    public void testExplode(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         String explode = "paths.'/user/{username}'.put.responses.'200'.content.'application/xml'.encoding.password.explode";
         vr.body(explode, equalTo(true));
         explode = "paths.'/user/{username}'.put.responses.'200'.content.'application/xml'.encoding.password.explode";
         vr.body(explode, equalTo(true));
     }
 
-    @Test
     @RunAsClient
-    public void testSecuritySchemesInComponents() throws InterruptedException {
-        String s = "components.securitySchemes";
-        vr.body(s, hasKey("httpTestScheme"));
-        vr.body(s, hasKey("airlinesRatingApp_auth"));
-        vr.body(s, hasKey("reviewoauth2"));
+    @Test(dataProvider = "formatProvider")
+    public void testCallbackAnnotations(String type) {
+        ValidatableResponse vr = callEndpoint(type);
+        String endpoint = "paths.'/streams'.post.callbacks";
+        vr.body(endpoint, hasKey("onData"));
+        vr.body(endpoint + ".onData", hasKey("{$request.query.callbackUrl}/data"));
+
+        endpoint = "paths.'/reviews'.post.callbacks";
+        vr.body(endpoint, hasKey("testCallback"));
+        vr.body(endpoint + ".testCallback", hasKey("http://localhost:9080/oas3-airlines/reviews"));
+
+        endpoint = "paths.'/bookings'.post.callbacks";
+        vr.body(endpoint, hasKey("get all the bookings"));
+        vr.body(endpoint + ".'get all the bookings'", hasKey("http://localhost:9080/airlines/bookings"));
+    }
 
-        vr.body(s + ".httpTestScheme.type", equalTo("http"));
-        vr.body(s + ".httpTestScheme.description", equalTo("user security scheme"));
-        vr.body(s + ".httpTestScheme.scheme", equalTo("testScheme"));
+    @RunAsClient
+    @Test(dataProvider = "formatProvider")
+    public void testCallbackOperationAnnotations(String type) {
+        ValidatableResponse vr = callEndpoint(type);
 
-        vr.body(s + ".airlinesRatingApp_auth.type", equalTo("apiKey"));
-        vr.body(s + ".airlinesRatingApp_auth.description", equalTo("authentication needed to access Airlines app"));
-        vr.body(s + ".airlinesRatingApp_auth.name", equalTo("api_key"));
-        vr.body(s + ".airlinesRatingApp_auth.in", equalTo("header"));
+        // TODO: cover /streams endpoint
+        String endpoint = "paths.'/bookings'.post.callbacks.'get all the bookings'.'http://localhost:9080/airlines/bookings'";
+        vr.body(endpoint, hasKey("get"));
+        vr.body(endpoint + ".get.summary", hasKey("Retrieve all bookings for current user"));
+        vr.body(endpoint + ".get.responses.'200'.description", equalTo("Bookings retrieved"));
+        vr.body(endpoint + "get.responses.'200'.content.'application/json'.type", equalTo("array"));
 
-        vr.body(s + ".reviewoauth2.type", equalTo("oauth2"));
-        vr.body(s + ".reviewoauth2.description", equalTo("authentication needed to create and delete reviews"));
+        endpoint = "paths.'/reviews'.post.callbacks.testCallback.'http://localhost:9080/oas3-airlines/reviews'";
+        vr.body(endpoint, hasKey("get"));
 
-        String t = "components.securitySchemes.reviewoauth2.flows";
-        vr.body(t + ".implicit.authorizationUrl", equalTo("https://example.com/api/oauth/dialog"));
-        vr.body(t + ".implicit.scopes.'write:reviews'", equalTo("create a review"));
-        vr.body(t + ".authorizationCode.authorizationUrl", equalTo("https://example.com/api/oauth/dialog"));
-        vr.body(t + ".authorizationCode.tokenUrl", equalTo("https://example.com/api/oauth/token"));
-        vr.body(t + ".password.refreshUrl", equalTo("https://example.com/api/oauth/refresh"));
-        vr.body(t + ".clientCredentials.authorizationUrl", equalTo("https://example.com/api/oauth/clientcredentials"));
-        vr.body(t + ".clientCredentials.scopes.'read:reviews'", equalTo("search for a review"));
+        vr.body(endpoint + ".get.summary", equalTo("Get all reviews"));
+        vr.body(endpoint + ".get.operationId", equalTo("getAllReviews_1"));
+        vr.body(endpoint + "responses.'200'.description", equalTo("successful operation"));
+        vr.body(endpoint + "responses.'200'.content.'application/json'.schema.ref", equalTo("#/components/schemas/Review"));
     }
 
-    @Test
     @RunAsClient
-    public void testOAuthAnnotations() throws InterruptedException {
-        String endpoint = "components.securitySchemes.reviewoauth2.flows.";
+    @Test(dataProvider = "formatProvider")
+    public void testRequestBodyAnnotations(String type) {
+        ValidatableResponse vr = callEndpoint(type);
+        String endpoint = "paths.'/bookings'.post.requestBody";
+        vr.body(endpoint + ".description", equalTo("Create a new booking with the provided information."));
+        vr.body(endpoint + ".content", notNullValue());
 
-        vr.body(endpoint + "implicit.authorizationUrl", equalTo("https://example.com/api/oauth/dialog"));
-        vr.body(endpoint + "implicit.scopes.'write:reviews'", equalTo("create a review"));
+        endpoint = "paths.'/bookings/{id}'.put.requestBody";
+        vr.body(endpoint + ".content", notNullValue());
 
-        vr.body(endpoint + "authorizationCode.authorizationUrl", equalTo("https://example.com/api/oauth/dialog"));
-        vr.body(endpoint + "authorizationCode.tokenUrl", equalTo("https://example.com/api/oauth/token"));
+        endpoint = "paths.'/user'.post.requestBody";
+        vr.body(endpoint + ".description", equalTo("Record of a new user to be created in the system."));
+        vr.body(endpoint + ".content", notNullValue());
+        vr.body(endpoint + ".required", equalTo(true));
 
-        vr.body(endpoint + "password.tokenUrl", equalTo("https://example.com/api/oauth/token"));
-        vr.body(endpoint + "password.refreshUrl", equalTo("https://example.com/api/oauth/refresh"));
+        endpoint = "paths.'/user/{username}'.put.requestBody";
+        vr.body(endpoint + ".description", equalTo("Record of a new user to be created in the system."));
+        vr.body(endpoint + ".content", notNullValue());
 
-        vr.body(endpoint + "clientCredentials.tokenUrl", equalTo("https://example.com/api/oauth/token"));
-        vr.body(endpoint + "clientCredentials.scopes.'read:reviews'", equalTo("search for a review"));
+        endpoint = "paths.'/user/createWithArray'.post.requestBody";
+        vr.body(endpoint + ".description", equalTo("Array of user object"));
+        vr.body(endpoint + ".content", notNullValue());
+        vr.body(endpoint + ".required", equalTo(true));
+
+        endpoint = "paths.'/user/createWithList'.post.requestBody";
+        vr.body(endpoint + ".description", equalTo("List of user object"));
+        vr.body(endpoint + ".content", notNullValue());
+        vr.body(endpoint + ".required", equalTo(true));
     }
-    
-    @Test
+
     @RunAsClient
-    public void testSecurityRequirement() throws InterruptedException {
+    @Test(dataProvider = "formatProvider")
+    public void testSecurityRequirement(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         vr.body("security.airlinesRatingApp_auth[0][0]", equalTo(null));
 
         vr.body("paths.'/reviews'.post.security.reviewoauth2[0][0]", equalTo("write:reviews"));
@@ -523,9 +506,10 @@ public class EndpointTest {
         vr.body("paths.'/user/logout'.get.security.httpTestScheme", hasSize(1));
     }
 
-    @Test
     @RunAsClient
-    public void testSecuritySchemes() throws InterruptedException {
+    @Test(dataProvider = "formatProvider")
+    public void testSecuritySchemes(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         String s = "components.securitySchemes";
         vr.body(s, hasKey("httpTestScheme"));
         vr.body(s, hasKey("airlinesRatingApp_auth"));
@@ -533,9 +517,10 @@ public class EndpointTest {
         vr.body(s, hasKey("bookingSecurityScheme"));
     }
 
-    @Test
     @RunAsClient
-    public void testSecurityScheme() throws InterruptedException {
+    @Test(dataProvider = "formatProvider")
+    public void testSecurityScheme(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         String http = "components.securitySchemes.httpTestScheme.";
         vr.body(http + "type", equalTo("http"));
         vr.body(http + "description", equalTo("user security scheme"));
@@ -551,15 +536,16 @@ public class EndpointTest {
         vr.body(auth + "description", equalTo("authentication needed to access Airlines app"));
         vr.body(auth + "name", equalTo("api_key"));
         vr.body(auth + "in", equalTo("header"));
-        
+
         String reviewoauth2 = "components.securitySchemes.reviewoauth2.";
         vr.body(reviewoauth2 + "type", equalTo("oauth2"));
         vr.body(reviewoauth2 + "description", equalTo("authentication needed to create and delete reviews"));
     }
 
-    @Test
     @RunAsClient
-    public void testOAuthFlows() throws InterruptedException {
+    @Test(dataProvider = "formatProvider")
+    public void testOAuthFlows(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         String t = "components.securitySchemes.reviewoauth2.flows";
         vr.body(t, hasKey("implicit"));
         vr.body(t, hasKey("authorizationCode"));
@@ -567,9 +553,10 @@ public class EndpointTest {
         vr.body(t, hasKey("clientCredentials"));
     }
 
-    @Test
     @RunAsClient
-    public void testOAuthFlow() throws InterruptedException {
+    @Test(dataProvider = "formatProvider")
+    public void testOAuthFlow(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         String implicit = "components.securitySchemes.reviewoauth2.flows.implicit.";
         vr.body(implicit + "authorizationUrl", equalTo("https://example.com/api/oauth/dialog"));
 
@@ -581,22 +568,24 @@ public class EndpointTest {
         vr.body(password + "refreshUrl", equalTo("https://example.com/api/oauth/refresh"));
 
         String client = "components.securitySchemes.reviewoauth2.flows.clientCredentials.";
-        vr.body(client + "tokenUrl", equalTo("https://example.com/api/oauth/token"));   
+        vr.body(client + "tokenUrl", equalTo("https://example.com/api/oauth/token"));
     }
 
-    @Test
     @RunAsClient
-    public void testOAuthScope() throws InterruptedException {
+    @Test(dataProvider = "formatProvider")
+    public void testOAuthScope(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         String implicit = "components.securitySchemes.reviewoauth2.flows.implicit.";
         vr.body(implicit + "scopes.'write:reviews'", equalTo("create a review"));
 
         String client = "components.securitySchemes.reviewoauth2.flows.clientCredentials.";
-        vr.body(client + "scopes.'read:reviews'", equalTo("search for a review"));   
+        vr.body(client + "scopes.'read:reviews'", equalTo("search for a review"));
     }
 
-    @Test
     @RunAsClient
-    public void testEncodingRequestBody() throws InterruptedException {
+    @Test(dataProvider = "formatProvider")
+    public void testEncodingRequestBody(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         String s = "paths.'/user'.post.requestBody.content.'application/json'.encoding.email.";
         vr.body(s + "contentType", equalTo("text/plain"));
         vr.body(s + "style", equalTo("form"));
@@ -604,9 +593,10 @@ public class EndpointTest {
         vr.body(s + "allowReserved", equalTo(true));
     }
 
-    @Test
     @RunAsClient
-    public void testEncodingResponses() throws InterruptedException {
+    @Test(dataProvider = "formatProvider")
+    public void testEncodingResponses(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         String s = "paths.'/user/{username}'.put.responses.'200'.content.'application/json'.encoding.password.";
         vr.body(s + "contentType", equalTo("text/plain"));
         vr.body(s + "style", equalTo("form"));
@@ -620,9 +610,10 @@ public class EndpointTest {
         vr.body(t + "allowReserved", equalTo(true));
     }
 
-    @Test
     @RunAsClient
-    public void testLink() throws InterruptedException {
+    @Test(dataProvider = "formatProvider")
+    public void testLink(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         String s = "paths.'/user/{id}'.get.responses.'200'.links.'User name'.";
         vr.body(s + "operationId", equalTo("getUserByName"));
         vr.body(s + "description", equalTo("The username corresponding to provided user id"));
@@ -630,15 +621,16 @@ public class EndpointTest {
         String t = "paths.'/user/{id}'.get.responses.'200'.links.Review.";
         vr.body(t + "operationRef", equalTo("/db/reviews/{userName}"));
         vr.body(t + "description", equalTo("The reviews provided by user"));
-        
+
         String k = "paths.'/reviews'.post.responses.'201'.links.Review.";
         vr.body(k + "operationId", equalTo("getReviewById"));
         vr.body(k + "description", equalTo("get the review that was added"));
     }
 
-    @Test
     @RunAsClient
-    public void testLinkParameter() throws InterruptedException {
+    @Test(dataProvider = "formatProvider")
+    public void testLinkParameter(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         String s = "paths.'/user/{id}'.get.responses.'200'.links.'User name'.";
         vr.body(s + "parameters.userId", equalTo("$request.path.id"));
 
@@ -649,9 +641,10 @@ public class EndpointTest {
         vr.body(k + "parameters.reviewId", equalTo("$request.path.id"));
     }
 
-    @Test
     @RunAsClient
-    public void testSchema() {
+    @Test(dataProvider = "formatProvider")
+    public void testSchema(String type) {
+        ValidatableResponse vr = callEndpoint(type);
         // Basic properties
         vr.body("components.schemas.AirlinesRef.ref", equalTo("#/components/schemas/Airlines"));
         vr.body("components.schemas.Airlines.title", equalTo("Airlines"));
@@ -675,4 +668,217 @@ public class EndpointTest {
         vr.body("paths.‘/user/createWithArray’.post.parameters.‘userArray’.content.schema.minItems", equalTo(2));
         vr.body("paths.‘/user/createWithArray’.post.parameters.‘userArray’.content.schema.uniqueItems", equalTo(true));
     }
+
+    @RunAsClient
+    @Test(dataProvider = "formatProvider")
+    public void testExampleObject(String type) {
+        ValidatableResponse vr = callEndpoint(type);
+        // Example in Components
+        vr.body("components.examples.review.summary", equalTo("External review example"));
+        vr.body("components.examples.review.description", equalTo("This example exemplifies the content on our site."));
+        vr.body("components.examples.review.externalValue", equalTo("http://foo.bar/examples/review-example.json"));
+
+        // Example in Content
+        vr.body("paths.'/reviews/{user}'.parameters.find{ it.name=='user'}.content.examples.value", hasItem("bsmith"));
+
+        // Example in Parameter
+        vr.body("paths.'/reviews/{user}'.parameters.find{ it.name=='user'}.examples.value", hasItems("bsmith", "pat@example.com"));
+    }
+
+    @RunAsClient
+    @Test(dataProvider = "formatProvider")
+    public void testTagDeclarations(String type) {
+        ValidatableResponse vr = callEndpoint(type);
+        String tagsPath = "tags.find { it.name == '";
+        String desc = "' }.description";
+        vr.body(tagsPath + "user" + desc, equalTo("Operations about user"));
+        vr.body(tagsPath + "create" + desc, equalTo("Operations about create"));
+        vr.body(tagsPath + "Airlines" + desc, equalTo("All the airlines methods"));
+        vr.body(tagsPath + "Availability" + desc, equalTo("All the availability methods"));
+        vr.body(tagsPath + "Get Flights" + desc, equalTo("method to retrieve all flights available"));
+        vr.body(tagsPath + "Get Flights" + "' }.externalDocs.description", equalTo("A list of all the flights offered by the app"));
+        vr.body(tagsPath + "Get Flights" + "' }.externalDocs.url", equalTo("http://airlinesratingapp.com/ourflights"));
+        vr.body(tagsPath + "Bookings" + desc, equalTo("All the bookings methods"));
+        vr.body(tagsPath + "Reservations" + desc, equalTo("All the reservation methods"));
+        vr.body(tagsPath + "Reviews" + desc, equalTo("All the review methods"));
+        vr.body(tagsPath + "Ratings" + desc, equalTo("All the ratings methods"));
+    }
+
+    @RunAsClient
+    @Test(dataProvider = "formatProvider")
+    public void testTagsInOperations(String type) {
+        ValidatableResponse vr = callEndpoint(type);
+        vr.body("paths.'/availability'.get.tags", containsInAnyOrder("Get Flights", "Availability"));
+        vr.body("paths.'/bookings'.get.tags", containsInAnyOrder("bookings"));
+        vr.body("paths.'/bookings'.post.tags", containsInAnyOrder("Bookings", "Reservations"));
+        vr.body("paths.'/bookings/{id}'.get.tags", containsInAnyOrder("Bookings", "Reservations"));
+        vr.body("paths.'/bookings/{id}'.put.tags", containsInAnyOrder("Bookings", "Reservations"));
+        // no tag - class Tag was overwritten with empty Tag
+        vr.body("paths.'/bookings/{id}'.delete.tags", not(hasSize(greaterThan(0))));
+        vr.body("paths.'/reviews'.get.tags", containsInAnyOrder("Reviews", "Ratings"));
+        vr.body("paths.'/reviews'.post.tags", containsInAnyOrder("Reviews"));
+        vr.body("paths.'/reviews/{id}'.get.tags", containsInAnyOrder("Reviews", "Ratings"));
+        vr.body("paths.'/reviews/{id}'.delete.tags", containsInAnyOrder("Reviews", "Ratings"));
+        vr.body("paths.'/reviews/{user}'.get.tags", containsInAnyOrder("Reviews", "Ratings"));
+        vr.body("paths.'/reviews/{airline}'.get.tags", containsInAnyOrder("Reviews", "Ratings"));
+        vr.body("paths.'/reviews/{user}/{airlines}'.get.tags", containsInAnyOrder("Reviews", "Ratings"));
+        vr.body("paths.'/user'.post.tags", containsInAnyOrder("user", "create"));
+        vr.body("paths.'/user/createWithArray'.post.tags", containsInAnyOrder("user", "create"));
+        vr.body("paths.'/user/createWithList'.post.tags", containsInAnyOrder("user", "create"));
+        vr.body("paths.'/user/{username}'.get.tags", containsInAnyOrder("user"));
+        vr.body("paths.'/user/{username}'.put.tags", containsInAnyOrder("user"));
+        vr.body("paths.'/user/{username}'.delete.tags", containsInAnyOrder("user"));
+        vr.body("paths.'/user/{id}'.get.tags", containsInAnyOrder("user"));
+        // empty tag was defined
+        vr.body("paths.'/user/login'.get.tags", not(hasSize(greaterThan(0))));
+        // no tag was defined
+        vr.body("paths.'/user/logout'.get.tags", not(hasSize(greaterThan(0))));
+        vr.body("paths.'/'.get.tags", containsInAnyOrder("Airlines"));
+    }
+
+    @RunAsClient
+    @Test(dataProvider = "formatProvider")
+    public void testComponents(String type) {
+        ValidatableResponse vr = callEndpoint(type);
+
+        // Tests to ensure that the reusable items declared using the
+        // @Components annotation (within @OpenAPIDefinition) exist.
+        // Each of these item types are tested elsewhere, so no need to test the
+        // content of them here.
+        vr.body("components.schemas.Bookings", notNullValue());
+        vr.body("components.schemas.Airlines", notNullValue());
+        vr.body("components.schemas.AirlinesRef", notNullValue());
+        vr.body("components.responses.FoundAirlines", notNullValue());
+        vr.body("components.responses.FoundBookings", notNullValue());
+        vr.body("components.parameters.departureDate", notNullValue());
+        vr.body("components.parameters.username", notNullValue());
+        vr.body("components.examples.review", notNullValue());
+        vr.body("components.examples.user", notNullValue());
+        vr.body("components.requestBodies.review", notNullValue());
+        vr.body("components.headers.Max-Rate", notNullValue());
+        vr.body("components.headers.Request-Limit", notNullValue());
+        vr.body("components.securitySchemes.httpTestScheme", notNullValue());
+        vr.body("components.links.UserName", notNullValue());
+        vr.body("components.callbacks.GetBookings", notNullValue());
+    }
+
+    @RunAsClient
+    @Test(dataProvider = "formatProvider")
+    public void testHeaderInAPIResponse(String type) {
+        ValidatableResponse vr = callEndpoint(type);
+
+        // Headers within APIResponse
+        String responseHeader1 = "paths.'/reviews/{id}'.get.responses.'200'.headers.responseHeader1";
+        vr.body(responseHeader1, notNullValue());
+        vr.body(responseHeader1 + ".description", equalTo("Max rate"));
+        vr.body(responseHeader1 + ".required", equalTo(true));
+        vr.body(responseHeader1 + ".deprecated", equalTo(true));
+        vr.body(responseHeader1 + ".allowEmptyValue", equalTo(true));
+        vr.body(responseHeader1 + ".style", equalTo("simple"));
+        vr.body(responseHeader1 + ".schema.type", equalTo("integer"));
+
+        String responseHeader2 = "paths.'/reviews/{id}'.get.responses.'200'.headers.responseHeader2";
+        vr.body(responseHeader2, notNullValue());
+        vr.body(responseHeader2 + ".description", equalTo("Input value"));
+        vr.body(responseHeader2 + ".required", equalTo(true));
+        vr.body(responseHeader2 + ".deprecated", equalTo(true));
+        vr.body(responseHeader2 + ".allowEmptyValue", equalTo(true));
+        vr.body(responseHeader2 + ".style", equalTo("simple"));
+        vr.body(responseHeader2 + ".schema.type", equalTo("string"));
+    }
+
+    @RunAsClient
+    @Test(dataProvider = "formatProvider")
+    public void testHeaderInEncoding(String type) {
+        ValidatableResponse vr = callEndpoint(type);
+
+        // Header within Encoding
+        String testHeader = "paths.'/user'.post.requestBody.content.'application/json'.encoding.email.headers.testHeader";
+        vr.body(testHeader, notNullValue());
+        vr.body(testHeader + ".description", equalTo("Minimum rate"));
+        vr.body(testHeader + ".required", equalTo(true));
+        vr.body(testHeader + ".deprecated", equalTo(true));
+        vr.body(testHeader + ".allowEmptyValue", equalTo(true));
+        vr.body(testHeader + ".style", equalTo("simple"));
+        vr.body(testHeader + ".schema.type", equalTo("integer"));
+    }
+
+    @RunAsClient
+    @Test(dataProvider = "formatProvider")
+    public void testRefHeaderInAPIResponse(String type) {
+        ValidatableResponse vr = callEndpoint(type);
+
+        // Reference to Header within APIResponse
+        String responseRefHeader = "paths.'/reviews'.get.responses.'200'.headers.Request-Limit";
+        vr.body(responseRefHeader, notNullValue());
+        vr.body(responseRefHeader + ".$ref", equalTo("#/components/headers/Request-Limit"));
+    }
+
+    @RunAsClient
+    @Test(dataProvider = "formatProvider")
+    public void testRefHeaderInEncoding(String type) {
+        ValidatableResponse vr = callEndpoint(type);
+
+        // Reference to Header within Encoding
+        String encodingRefHeader = "paths.'/user/{username}'.put.responses.'200'.content.'application/json'.encoding.password.headers.Max-Rate";
+        vr.body(encodingRefHeader, notNullValue());
+        vr.body(encodingRefHeader + ".$ref", equalTo("#/components/headers/Max-Rate"));
+    }
+
+    @RunAsClient
+    @Test(dataProvider = "formatProvider")
+    public void testHeaderInComponents(String type) {
+        ValidatableResponse vr = callEndpoint(type);
+        String maxRate = "components.headers.Max-Rate";
+        vr.body(maxRate + ".description", equalTo("Maximum rate"));
+        vr.body(maxRate + ".required", equalTo(true));
+        vr.body(maxRate + ".deprecated", equalTo(true));
+        vr.body(maxRate + ".allowEmptyValue", equalTo(true));
+        vr.body(maxRate + ".style", equalTo("simple"));
+        vr.body(maxRate + ".schema.type", equalTo("integer"));
+    }
+
+    @RunAsClient
+    @Test(dataProvider = "formatProvider")
+    public void testContentInAPIResponse(String type) {
+        ValidatableResponse vr = callEndpoint(type);
+
+        String content1 = "paths.'/availability'.get.responses.'200'.content.'application/json'";
+        vr.body(content1, notNullValue());
+        vr.body(content1 + ".schema.type", equalTo("array"));
+        vr.body(content1 + ".schema.items.$ref", equalTo("#/components/schemas/Flight"));
+
+        String content2 = "paths.'/user/{username}'.put.responses.'200'.content";
+        vr.body(content2, notNullValue());
+        vr.body(content2 + ".'application/json'", notNullValue());
+        vr.body(content2 + ".'application/json'.schema.$ref", equalTo("#/components/schemas/User"));
+        vr.body(content2 + ".'application/json'.encoding.password", notNullValue());
+
+        vr.body(content2, notNullValue());
+        vr.body(content2 + ".'application/xml'", notNullValue());
+        vr.body(content2 + ".'application/xml'.schema.$ref", equalTo("#/components/schemas/User"));
+        vr.body(content2 + ".'application/xml'.encoding.password", notNullValue());
+    }
+
+    @RunAsClient
+    @Test(dataProvider = "formatProvider")
+    public void testContentInRequestBody(String type) {
+        ValidatableResponse vr = callEndpoint(type);
+
+        String contentJson = "paths.'/bookings'.post.requestBody.content.'application/json'";
+        vr.body(contentJson, notNullValue());
+        vr.body(contentJson + ".schema.$ref", equalTo("#/components/schemas/Booking"));
+        vr.body(contentJson + ".examples.booking.summary", equalTo("External booking example"));
+    }
+
+    @RunAsClient
+    @Test(dataProvider = "formatProvider")
+    public void testContentInParameter(String type) {
+        ValidatableResponse vr = callEndpoint(type);
+
+        String content = "paths.'/reviews/{user}'.get.parameters.find{ it.name == 'user' }.content";
+        vr.body(content, notNullValue());
+        vr.body(content + ".'*/*'", notNullValue());
+        vr.body(content + ".'*/*'.schema.type", equalTo("string"));
+    }
 }
diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/AppTestBase.java b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/AppTestBase.java
new file mode 100644
index 0000000..3be3603
--- /dev/null
+++ b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/AppTestBase.java
@@ -0,0 +1,121 @@
+/**
+ * Copyright (c) 2017 Contributors to the Eclipse Foundation
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.eclipse.microprofile.openapi.tck;
+
+import static io.restassured.RestAssured.given;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.eclipse.microprofile.openapi.tck.utils.YamlToJsonConverterServlet;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.testng.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+
+import io.restassured.RestAssured;
+import io.restassured.parsing.Parser;
+import io.restassured.response.ValidatableResponse;
+
+public abstract class AppTestBase extends Arquillian {
+    private static final String APPLICATION_JSON = "application/json";
+
+    private static final String DEFAULT_PROTOCOL = "http";
+    private static final String DEFAULT_HOST = "localhost";
+    private static final int DEFAULT_PORT = 9080;
+
+    private static String serverUrl;
+    private static String username;
+    private static String password;
+    private static Map<String, String> headers = new HashMap<>();
+
+    @Deployment(name = "proxy")
+    public static WebArchive createProxy() {
+        return ShrinkWrap.create(WebArchive.class, "proxy.war")
+                .addClass(YamlToJsonConverterServlet.class)
+                .addAsLibraries(new File("./lib/httpclient-4.5.2.jar"))
+                .addAsLibraries(new File("./lib/httpcore-4.4.4.jar"))
+                .addAsLibraries(new File("./lib/jackson-core-2.9.2.jar"))
+                .addAsLibraries(new File("./lib/jackson-dataformat-yaml-2.9.2.jar"))
+                .addAsLibraries(new File("./lib/jackson-databind-2.9.2.jar"))
+                .addAsLibraries(new File("./lib/jackson-annotations-2.9.1.jar"))
+                .addAsLibraries(new File("./lib/snakeyaml-1.18.jar"))
+                .addAsLibraries(new File("./lib/commons-logging-1.2.jar"))
+                .addAsLibraries(new File("./lib/commons-lang3-3.4.jar"));
+    }
+
+    @BeforeClass
+    public static void setUp() throws MalformedURLException {
+        // set base URI and port number to use for all requests
+        serverUrl = System.getProperty("test.url");
+        String protocol = DEFAULT_PROTOCOL;
+        String host = DEFAULT_HOST;
+        int port = DEFAULT_PORT;
+
+        if (serverUrl != null) {
+            URL url = new URL(serverUrl);
+            protocol = url.getProtocol();
+            host = url.getHost();
+            port = (url.getPort() == -1) ? DEFAULT_PORT : url.getPort();
+        }
+
+        RestAssured.baseURI = protocol + "://" + host;
+        RestAssured.port = port;
+
+        username = System.getProperty("test.user");
+        password = System.getProperty("test.pwd");
+
+        if (username != null && password != null) {
+            RestAssured.authentication = RestAssured.basic(username, password);
+            RestAssured.useRelaxedHTTPSValidation();
+        }
+        RestAssured.defaultParser = Parser.JSON;
+
+        if (StringUtils.isBlank(serverUrl)) {
+            serverUrl = DEFAULT_PROTOCOL + "://" + DEFAULT_HOST + ":" + DEFAULT_PORT;
+        }
+        headers.put(YamlToJsonConverterServlet.TCK_HEADER_SERVERURL, serverUrl);
+        if (StringUtils.isNotBlank(username)) {
+            headers.put(YamlToJsonConverterServlet.TCK_HEADER_USERNAME, username);
+        }
+        if (StringUtils.isNotBlank(password)) {
+            headers.put(YamlToJsonConverterServlet.TCK_HEADER_PASSWORD, password);
+        }
+    }
+
+    public ValidatableResponse callEndpoint(String type) {
+        ValidatableResponse vr;
+        if ("JSON".equals(type)) {
+            vr = given().accept(APPLICATION_JSON).when().get("/openapi").then().statusCode(200);
+        }
+        else {
+            vr = given().headers(headers).when().get("/proxy").then().statusCode(200);
+        }
+        return vr;
+    }
+
+    @DataProvider(name = "formatProvider")
+    public Object[][] provide() throws Exception {
+        return new Object[][] { { "JSON" }, { "YAML" } };
+    }
+}
diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/EndpointTestPetStore.java b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/EndpointTestPetStore.java
deleted file mode 100644
index d1c7424..0000000
--- a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/EndpointTestPetStore.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/**
- * Copyright (c) 2017 Contributors to the Eclipse Foundation
- * <p>
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * <p>
- * http://www.apache.org/licenses/LICENSE-2.0
- * <p>
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.eclipse.microprofile.openapi.tck;
-
-import static io.restassured.RestAssured.given;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.hasEntry;
-import static org.hamcrest.Matchers.hasItems;
-import static org.hamcrest.Matchers.nullValue;
-
-import java.io.File;
-import java.net.MalformedURLException;
-import java.net.URL;
-
-import org.eclipse.microprofile.openapi.tck.utils.YamlToJsonConverterServlet;
-import org.jboss.arquillian.container.test.api.Deployment;
-import org.jboss.arquillian.container.test.api.RunAsClient;
-import org.jboss.arquillian.junit.Arquillian;
-import org.jboss.shrinkwrap.api.ShrinkWrap;
-import org.jboss.shrinkwrap.api.asset.EmptyAsset;
-import org.jboss.shrinkwrap.api.spec.WebArchive;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import io.restassured.RestAssured;
-import io.restassured.parsing.Parser;
-import io.restassured.response.Response;
-import io.restassured.response.ValidatableResponse;
-
-@RunWith(Arquillian.class)
-public class EndpointTestPetStore {
-    private static final String APPLICATION_JSON = "application/json";
-    private static final String APPLICATION_YAML = "application/yaml";
-
-    private static final String DEFAULT_PROTOCOL = "http";
-    private static final String DEFAULT_HOST = "localhost";
-    private static final int DEFAULT_PORT = 9080;
-
-    private static ValidatableResponse vr;
-
-    @BeforeClass
-    static public void setup() throws MalformedURLException {
-        // set base URI and port number to use for all requests
-        String serverUrl = System.getProperty("test.url");
-        String protocol = DEFAULT_PROTOCOL;
-        String host = DEFAULT_HOST;
-        int port = DEFAULT_PORT;
-
-        if (serverUrl != null) {
-            URL url = new URL(serverUrl);
-            protocol = url.getProtocol();
-            host = url.getHost();
-            port = (url.getPort() == -1) ? DEFAULT_PORT : url.getPort();
-        }
-
-        RestAssured.baseURI = protocol + "://" + host;
-        RestAssured.port = port;
-
-        String userName = System.getProperty("test.user");
-        String password = System.getProperty("test.pwd");
-
-        if (userName != null && password != null) {
-            RestAssured.authentication = RestAssured.basic(userName, password);
-            RestAssured.useRelaxedHTTPSValidation();
-        }
-        RestAssured.defaultParser = Parser.JSON;
-    }
-
-    @Before
-    public void setUpTest() {
-        vr = given().when().get("/proxy").then().statusCode(200);
-    }
-
-    @Deployment(name = "proxy")
-    public static WebArchive createProxy() {
-        WebArchive war = ShrinkWrap.create(WebArchive.class, "proxy.war")
-                .addClass(YamlToJsonConverterServlet.class)
-                .addAsLibraries(new File("./lib/httpclient-4.5.2.jar"))
-                .addAsLibraries(new File("./lib/httpcore-4.4.4.jar"))
-                .addAsLibraries(new File("./lib/jackson-core-2.9.2.jar"))
-                .addAsLibraries(new File("./lib/jackson-dataformat-yaml-2.9.2.jar"))
-                .addAsLibraries(new File("./lib/jackson-databind-2.9.2.jar"))
-                .addAsLibraries(new File("./lib/jackson-annotations-2.9.1.jar"))
-                .addAsLibraries(new File("./lib/snakeyaml-1.18.jar"))
-                .addAsLibraries(new File("./lib/commons-logging-1.2.jar"))
-                .addAsLibraries(new File("./lib/commons-lang3-3.4.jar"));
-        return war;
-    }
-
-    @Deployment(name = "petstore")
-    public static WebArchive createDeployment() {
-        WebArchive war = ShrinkWrap.create(WebArchive.class, "petstore.war")
-                .addPackages(true, "org.eclipse.microprofile.openapi.apps.petstore")
-                .addAsManifestResource(EmptyAsset.INSTANCE, "/openapi/openapi.yaml");
-        return war;
-    }
-
-    @Test
-    @RunAsClient
-    public void testVersion() throws InterruptedException {
-        vr.body("openapi", equalTo("3.0.0"));
-    }
-
-    @Test
-    @RunAsClient
-    public void testSchema() throws InterruptedException {
-        // Schema annotation tests
-        Response response = given().when().get("/proxy");
-        System.out.println("response=" + response.asString());
-        ValidatableResponse vr = response.then().parser("", Parser.JSON).statusCode(200);
-        
-        // Basic properties
-        vr.body("paths.'/pet/findByTags'.get.parameters.find{ it.name == 'tags' }.schema.default", equalTo("Dog"));  // default val
-        vr.body("paths.'/pet/findByTags'.get.parameters.find{ it.name == 'tags' }.schema.enum", hasItems("Cat", "Dog", "Lizard")); // enumeration 
-        vr.body("paths.'/pet/findByTags'.get.parameters.find{ it.name == 'tags' }.schema.externalDocs.description", equalTo("Pet Types")); // ext doc
-        vr.body("paths.'/pet/findByTags'.get.parameters.find{ it.name == 'tags' }.schema.deprecated", equalTo(true)); // deprecated
-        vr.body("paths.'/store/order/{orderId}'.get.responses.'900'.schema", nullValue()); // hidden
-        
-        // Numerical properties
-        vr.body("paths.'/pet/{petId}'.get.parameters.find{ it.name == 'petId' }.schema.maximum", equalTo(101)); // maximum
-        vr.body("paths.'/pet/{petId}'.get.parameters.find{ it.name == 'petId' }.schema.exclusiveMaximum", equalTo(true)); // exclusive max
-        vr.body("paths.'/pet/{petId}'.get.parameters.find{ it.name == 'petId' }.schema.minimum", equalTo(9)); // minimum
-        vr.body("paths.'/pet/{petId}'.get.parameters.find{ it.name == 'petId' }.schema.exclusiveMinimum", equalTo(true)); // exclusive min
-        vr.body("paths.'/pet/{petId}'.get.parameters.find{ it.name == 'petId' }.schema.multipleOf", equalTo(10)); // multiple of
-        
-        //  String properties
-        vr.body("paths.'/pet/{petId}'.delete.parameters.find{ it.name == 'apiKey' }.schema.maxLength", equalTo(256)); // max length
-        vr.body("paths.'/pet/{petId}'.delete.parameters.find{ it.name == 'apiKey' }.schema.minLength", equalTo(32)); // min length
-
-        // Other properties
-        String storeOrderResponses = "paths.'/store/order/{orderId}'.get.responses";
-        vr.body("paths.'/pet/{petId}'.get.responses.'200'.content.'application/json'.schema.readonly", equalTo(true)); //  readonly
-        vr.body(storeOrderResponses + ".'404'.content.'application/json'.schema.anyOf", hasItems("Order.class", "BadOrder.class")); //    any of
-        vr.body(storeOrderResponses + ".'200'.content.'application/json'.schema.allOf", hasItems("Order.class", "Pet.class")); //    all of
-        vr.body("paths.'/pet/{petId}'.get.responses.'200'.content.'application/json'.schema.oneOf", 
-                hasItems("Cat.class", "Dog.class", "Lizard.class")); //    one of
-        vr.body(storeOrderResponses + ".'200'.content.'application/json'.schema.not", equalTo("BadOrder.class")); //    not
-        vr.body(storeOrderResponses + ".'404'.content.'application/json'.schema.discriminatorProperty", equalTo("id")); //    discrimination prop
-        vr.body(storeOrderResponses + ".'404'.content.'application/json'.schema.discriminatorMapping", hasEntry("0", "BadOrder.class")); // mapping
-    }
-}
diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelConstructionTest.java b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelConstructionTest.java
index cd941bf..4d41fed 100644
--- a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelConstructionTest.java
+++ b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelConstructionTest.java
@@ -16,6 +16,14 @@
 
 package org.eclipse.microprofile.openapi.tck;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNotSame;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertSame;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
 import java.beans.Introspector;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -69,20 +77,17 @@ import org.eclipse.microprofile.openapi.models.servers.ServerVariable;
 import org.eclipse.microprofile.openapi.models.servers.ServerVariables;
 import org.eclipse.microprofile.openapi.models.tags.Tag;
 import org.jboss.arquillian.container.test.api.Deployment;
-import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.testng.Arquillian;
 import org.jboss.shrinkwrap.api.ShrinkWrap;
 import org.jboss.shrinkwrap.api.spec.WebArchive;
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import org.testng.annotations.Test;
 
 /**
  * This test covers construction of the OpenAPI model. It verifies that the implementation can
  * create instances of all of the Constructible interfaces and then invokes methods (including
  * getters, setters and builders) on those instances to verify that they behave correctly.
  */
-@RunWith(Arquillian.class)
-public class ModelConstructionTest {
+public class ModelConstructionTest extends Arquillian {
     
     private static final Map<String,Property> PARAMETER_PROPERTIES = Collections.unmodifiableMap(collectProperties0(Parameter.class));
     
@@ -121,7 +126,7 @@ public class ModelConstructionTest {
                 return getter.invoke(target);
             }
             catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
-                Assert.fail("Invocation of getter method \"" + getter.getName() + "\" failed: " + e.getMessage());
+                fail("Invocation of getter method \"" + getter.getName() + "\" failed: " + e.getMessage());
                 throw new RuntimeException(e);
             }
         }
@@ -130,7 +135,7 @@ public class ModelConstructionTest {
                 setter.invoke(target, value);
             }
             catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
-                Assert.fail("Invocation of setter method \"" + setter.getName() + "\" failed: " + e.getMessage());
+                fail("Invocation of setter method \"" + setter.getName() + "\" failed: " + e.getMessage());
                 throw new RuntimeException(e);
             }
         }
@@ -139,7 +144,7 @@ public class ModelConstructionTest {
                 return builder.invoke(target, value);
             }
             catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
-                Assert.fail("Invocation of builder method \"" + builder.getName() + "\" failed: " + e.getMessage());
+                fail("Invocation of builder method \"" + builder.getName() + "\" failed: " + e.getMessage());
                 throw new RuntimeException(e);
             }
         }
@@ -153,13 +158,12 @@ public class ModelConstructionTest {
             return getter != null && setter != null;
         }
     }
-    
+
     @Deployment
-    public static WebArchive createProxy() {
-        WebArchive war = ShrinkWrap.create(WebArchive.class);
-        return war;
+    public static WebArchive createDeployment() {
+        return ShrinkWrap.create(WebArchive.class);
     }
-    
+
     @Test
     public void componentsTest() {
         final Components c = processConstructible(Components.class);
@@ -277,10 +281,10 @@ public class ModelConstructionTest {
         
         final String pathItemKey2 = "myPathItem2";
         final PathItem pathItemValue2 = createConstructibleInstance(PathItem.class);
-        Assert.assertNull("No previous mapping expected.", p.put(pathItemKey2, pathItemValue2));
+        assertNull(p.put(pathItemKey2, pathItemValue2), "No previous mapping expected.");
         checkMapEntry(p, pathItemKey2, pathItemValue2);
         
-        Assert.assertEquals("The map is expected to contain two entries.", 2, p.size());
+        assertEquals(p.size(), 2, "The map is expected to contain two entries.");
     }
     
     @Test
@@ -294,10 +298,10 @@ public class ModelConstructionTest {
         
         final String pathItemKey2 = "myPathItem2";
         final PathItem pathItemValue2 = createConstructibleInstance(PathItem.class);
-        Assert.assertNull("No previous mapping expected.", c.put(pathItemKey2, pathItemValue2));
+        assertNull(c.put(pathItemKey2, pathItemValue2), "No previous mapping expected.");
         checkMapEntry(c, pathItemKey2, pathItemValue2);
         
-        Assert.assertEquals("The map is expected to contain two entries.", 2, c.size());
+        assertEquals(c.size(), 2, "The map is expected to contain two entries.");
     }
     
     @Test
@@ -351,10 +355,10 @@ public class ModelConstructionTest {
         
         final String mediaTypeKey2 = "myPathItem2";
         final MediaType mediaTypeValue2 = createConstructibleInstance(MediaType.class);
-        Assert.assertNull("No previous mapping expected.", c.put(mediaTypeKey2, mediaTypeValue2));
+        assertNull(c.put(mediaTypeKey2, mediaTypeValue2), "No previous mapping expected.");
         checkMapEntry(c, mediaTypeKey2, mediaTypeValue2);
         
-        Assert.assertEquals("The map is expected to contain two entries.", 2, c.size());
+        assertEquals(c.size(), 2, "The map is expected to contain two entries.");
     }
     
     @Test
@@ -477,10 +481,10 @@ public class ModelConstructionTest {
         
         final String responseKey2 = "myResponse2";
         final APIResponse responseValue2 = createConstructibleInstance(APIResponse.class);
-        Assert.assertNull("No previous mapping expected.", responses.put(responseKey2, responseValue2));
+        assertNull(responses.put(responseKey2, responseValue2), "No previous mapping expected.");
         checkMapEntry(responses, responseKey2, responseValue2);
         
-        Assert.assertEquals("The map is expected to contain two entries.", 2, responses.size());
+        assertEquals(responses.size(), 2, "The map is expected to contain two entries.");
     }
     
     @Test
@@ -504,10 +508,10 @@ public class ModelConstructionTest {
         
         final String scopeKey2 = "myScope2";
         final String scopeValue2 = new String("myDescription2");
-        Assert.assertNull("No previous mapping expected.", s.put(scopeKey2, scopeValue2));
+        assertNull(s.put(scopeKey2, scopeValue2), "No previous mapping expected.");
         checkMapEntry(s, scopeKey2, scopeValue2);
         
-        Assert.assertEquals("The map is expected to contain two entries.", 2, s.size());
+        assertEquals(s.size(), 2, "The map is expected to contain two entries.");
     }
     
     @Test
@@ -521,10 +525,10 @@ public class ModelConstructionTest {
         
         final String schemeKey2 = "myResponse2";
         final List<String> schemeValue2 = new ArrayList<String>();
-        Assert.assertNull("No previous mapping expected.", sr.put(schemeKey2, schemeValue2));
+        assertNull(sr.put(schemeKey2, schemeValue2), "No previous mapping expected.");
         checkMapEntry(sr, schemeKey2, schemeValue2);
         
-        Assert.assertEquals("The map is expected to contain two entries.", 2, sr.size());
+        assertEquals(sr.size(), 2, "The map is expected to contain two entries.");
     }
     
     @Test
@@ -557,10 +561,10 @@ public class ModelConstructionTest {
         
         final String varKey2 = "myServerVariable2";
         final ServerVariable varValue2 = createConstructibleInstance(ServerVariable.class);
-        Assert.assertNull("No previous mapping expected.", svs.put(varKey2, varValue2));
+        assertNull(svs.put(varKey2, varValue2), "No previous mapping expected.");
         checkMapEntry(svs, varKey2, varValue2);
         
-        Assert.assertEquals("The map is expected to contain two entries.", 2, svs.size());
+        assertEquals(svs.size(), 2, "The map is expected to contain two entries.");
     }
     
     @Test
@@ -586,12 +590,12 @@ public class ModelConstructionTest {
     private <T extends Constructible> T createConstructibleInstance(Class<T> clazz) {
         // Check that the OASFactory is able to create an instance of the given Class.
         final T o1 = OASFactory.createObject(clazz);
-        Assert.assertNotNull("The return value of OASFactory.createObject(" + clazz.getName() + ") must not be null.", o1);
-        Assert.assertTrue("The return value of OASFactory.createObject() is expected to be an instance of: " + clazz.getName(), clazz.isInstance(o1));
+        assertNotNull(o1, "The return value of OASFactory.createObject(" + clazz.getName() + ") must not be null.");
+        assertTrue(clazz.isInstance(o1), "The return value of OASFactory.createObject() is expected to be an instance of: " + clazz.getName());
         final T o2 = OASFactory.createObject(clazz);
-        Assert.assertNotNull("The return value of OASFactory.createObject(" + clazz.getName() + ") must not be null.", o2);
-        Assert.assertTrue("The return value of OASFactory.createObject() is expected to be an instance of: " + clazz.getName(), clazz.isInstance(o2));
-        Assert.assertNotSame("OASFactory.createObject(" + clazz.getName() + ") is expected to create a new object on each invocation.", o1, o2);
+        assertNotNull(o2, "The return value of OASFactory.createObject(" + clazz.getName() + ") must not be null.");
+        assertTrue(clazz.isInstance(o2), "The return value of OASFactory.createObject() is expected to be an instance of: " + clazz.getName());
+        assertNotSame(o2, o1, "OASFactory.createObject(" + clazz.getName() + ") is expected to create a new object on each invocation.");
         return o1;
     }
     
@@ -604,45 +608,45 @@ public class ModelConstructionTest {
         e.addExtension(extensionName1, obj1);
         e.addExtension(extensionName2, obj2);
         final Map<String, Object> map = e.getExtensions();
-        Assert.assertEquals("The extensions map is expected to contain two entries.", 2, map.size());
-        Assert.assertTrue("The extensions map is expected to contain the key: " + extensionName1, map.containsKey(extensionName1));
-        Assert.assertTrue("The extensions map is expected to contain the key: " + extensionName2, map.containsKey(extensionName2));
-        Assert.assertSame("The value associated with the key: " + extensionName1 + " is expected to be the same one that was added.",
-                obj1, map.get(extensionName1));
-        Assert.assertSame("The value associated with the key: " + extensionName2 + " is expected to be the same one that was added.",
-                obj2, map.get(extensionName2));
+        assertEquals(map.size(), 2, "The extensions map is expected to contain two entries.");
+        assertTrue(map.containsKey(extensionName1), "The extensions map is expected to contain the key: " + extensionName1);
+        assertTrue(map.containsKey(extensionName2), "The extensions map is expected to contain the key: " + extensionName2);
+        assertSame(map.get(extensionName1), obj1,
+                "The value associated with the key: " + extensionName1 + " is expected to be the same one that was added.");
+        assertSame(map.get(extensionName2), obj2,
+                "The value associated with the key: " + extensionName2 + " is expected to be the same one that was added.");
         // Check that the extension map can be replaced with the setter and that it is returned by the getter.
         final Map<String, Object> newMap = new HashMap<>();
         e.setExtensions(newMap);
         final Map<String, Object> map2 = e.getExtensions();
-        Assert.assertEquals("The extensions map is expected to contain no entries.", 0, map2.size());
-        Assert.assertSame("The return value of getExtensions() is expected to be the same value that was set.", newMap, map2);
+        assertEquals(map2.size(), 0, "The extensions map is expected to contain no entries.");
+        assertSame(map2, newMap, "The return value of getExtensions() is expected to be the same value that was set.");
     }
     
     private void processReference(Reference<?> r) {
         // Check that the ref value can be set using the setter method and that the getter method returns the same value.
         final String myRef1 = createReference(r, "myRef1");
         r.setRef(myRef1);
-        Assert.assertEquals("The return value of getRef() is expected to be equal to the value that was set.", myRef1, r.getRef());
+        assertEquals(r.getRef(), myRef1, "The return value of getRef() is expected to be equal to the value that was set.");
         // Check that the short name ref value can be set using the setter method and that the getter method returns the expanded value.
         if (!(r instanceof PathItem)) {
             final String shortName = "myRef2";
             final String myRef2 = createReference(r, shortName);
             r.setRef(shortName);
-            Assert.assertEquals("The return value of getRef() is expected to be a fully expanded name.", myRef2, r.getRef());
+            assertEquals(r.getRef(), myRef2, "The return value of getRef() is expected to be a fully expanded name.");
         }
         // Check that the ref value can be set using the builder method and that the getter method returns the same value.
         final String myRef3 = createReference(r, "myRef3");
         final Reference<?> self = r.ref(myRef3);
-        Assert.assertSame("The return value of ref() is expected to return the current instance.", r, self);
-        Assert.assertEquals("The return value of getRef() is expected to be equal to the value that was set.", myRef3, r.getRef());
+        assertSame(self, r, "The return value of ref() is expected to return the current instance.");
+        assertEquals(r.getRef(), myRef3, "The return value of getRef() is expected to be equal to the value that was set.");
         // Check that the short name ref value can be set using the builder method and that the getter method returns the expanded value.
         if (!(r instanceof PathItem)) {
             final String shortName = "myRef4";
             final String myRef4 = createReference(r, shortName);
             final Reference<?> self2 = r.ref(shortName);
-            Assert.assertSame("The return value of ref() is expected to return the current instance.", r, self2);
-            Assert.assertEquals("The return value of getRef() is expected to be a fully expanded name.", myRef4, r.getRef());
+            assertSame(self2, r, "The return value of ref() is expected to return the current instance.");
+            assertEquals(r.getRef(), myRef4, "The return value of getRef() is expected to be a fully expanded name.");
         }
     }
     
@@ -650,30 +654,30 @@ public class ModelConstructionTest {
         final Object value1 = getInstanceOf(p.getType(), false);
         p.invokeSetter(o, value1);
         if (!p.isPrimitive()) {
-            Assert.assertSame("The return value of the getter method for property \"" + 
+            assertSame(p.invokeGetter(o), value1, "The return value of the getter method for property \"" + 
                     p.getName() + "\" of interface \"" + enclosingInterface.getName() +
-                    "\" is expected to be the same as the value that was set.", value1, p.invokeGetter(o));
+                    "\" is expected to be the same as the value that was set.");
         }
         else {
-            Assert.assertEquals("The return value of the getter method for property \"" + 
+            assertEquals(p.invokeGetter(o), value1, "The return value of the getter method for property \"" + 
                     p.getName() + "\" of interface \"" + enclosingInterface.getName() +
-                    "\" is expected to be equal to the value that was set.", value1, p.invokeGetter(o));
+                    "\" is expected to be equal to the value that was set.");
         }
         if (p.hasBuilder()) {
             final Object value2 = getInstanceOf(p.getType(), true);
             final Object self = p.invokeBuilder(o, value2);
-            Assert.assertSame("The return value of the builder method for property \"" + 
+            assertSame(self, o, "The return value of the builder method for property \"" + 
                     p.getName() + "\" of interface \"" + enclosingInterface.getName() +
-                    "\" is expected to be the same as the value that was set.", o, self);
+                    "\" is expected to be the same as the value that was set.");
             if (!p.isPrimitive()) {
-                Assert.assertSame("The return value of the getter method for property \"" + 
+                assertSame(p.invokeGetter(o), value2, "The return value of the getter method for property \"" + 
                         p.getName() + "\" of interface \"" + enclosingInterface.getName() +
-                        "\" is expected to be the same as the value that was set.", value2, p.invokeGetter(o));
+                        "\" is expected to be the same as the value that was set.");
             }
             else {
-                Assert.assertEquals("The return value of the getter method for property \"" + 
+                assertEquals(p.invokeGetter(o), value2, "The return value of the getter method for property \"" + 
                         p.getName() + "\" of interface \"" + enclosingInterface.getName() +
-                        "\" is expected to be equal to the value that was set.", value2, p.invokeGetter(o));
+                        "\" is expected to be equal to the value that was set.");
             }
         }
     }
@@ -851,23 +855,21 @@ public class ModelConstructionTest {
         checkMapEntry(p.getExamples(), exampleKey, exampleValue);
         
         // Check value of read-only 'in' property.
-        Assert.assertEquals("The value of the 'in' property is expected to be '" + name + "'.",
-                expectedIn, p.getIn());
+        assertEquals(p.getIn(), expectedIn, "The value of the 'in' property is expected to be '" + name + "'.");
     }
     
     private <T> void checkMapEntry(Map<String,T> map, String key, T value) {
-        Assert.assertNotNull("The map must not be null.", map);
-        Assert.assertTrue("The map is expected to contain the key: " + key, map.containsKey(key));
-        Assert.assertSame("The value associated with the key: " + key + " is expected to be the same one that was added.",
-                value, map.get(key));
+        assertNotNull(map, "The map must not be null.");
+        assertTrue(map.containsKey(key), "The map is expected to contain the key: " + key);
+        assertSame(map.get(key), value, "The value associated with the key: " + key + " is expected to be the same one that was added.");
     }
     
     private <T> void checkListEntry(List<T> list, T value) {
-        Assert.assertNotNull("The list must not be null.", list);
-        Assert.assertTrue("The list is expected to contain the value: " + value, list.stream().anyMatch((v) -> v == value));
+        assertNotNull(list, "The list must not be null.");
+        assertTrue(list.stream().anyMatch((v) -> v == value), "The list is expected to contain the value: " + value);
     }
     
     private <T> void checkSameObject(T expected, T actual) {
-        Assert.assertSame("Expecting same object.", expected, actual);
+        assertSame(actual, expected ,"Expecting same object.");
     }
 }
diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/OASFactoryErrorTest.java b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/OASFactoryErrorTest.java
index 5f10996..aed710f 100644
--- a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/OASFactoryErrorTest.java
+++ b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/OASFactoryErrorTest.java
@@ -22,15 +22,13 @@ import org.eclipse.microprofile.openapi.OASFactory;
 import org.eclipse.microprofile.openapi.models.Constructible;
 import org.eclipse.microprofile.openapi.models.info.License;
 import org.jboss.arquillian.container.test.api.Deployment;
-import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.testng.Arquillian;
 import org.jboss.shrinkwrap.api.ShrinkWrap;
 import org.jboss.shrinkwrap.api.spec.WebArchive;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import org.testng.annotations.Test;
+
+public class OASFactoryErrorTest extends Arquillian {
 
-@RunWith(Arquillian.class)
-public class OASFactoryErrorTest {
-    
     public interface MyConstructible extends Constructible {}
     public interface MyLicense extends License {}
     public abstract class MyAbstractLicenseImpl implements License {}
@@ -64,44 +62,43 @@ public class OASFactoryErrorTest {
             return null;
         }
     }
-    
+
     @Deployment
-    public static WebArchive createProxy() {
-        WebArchive war = ShrinkWrap.create(WebArchive.class);
-        return war;
+    public static WebArchive createDeployment() {
+        return ShrinkWrap.create(WebArchive.class);
     }
-    
-    @Test(expected = NullPointerException.class)
+
+    @Test(expectedExceptions = { NullPointerException.class })
     public void nullValueTest() {
         @SuppressWarnings("unused")
         final Object o = OASFactory.createObject(null);
     }
-    
-    @Test(expected = IllegalArgumentException.class)
+
+    @Test(expectedExceptions = { IllegalArgumentException.class })
     public void baseInterfaceTest() {
         @SuppressWarnings("unused")
         final Constructible c = OASFactory.createObject(Constructible.class);
     }
-    
-    @Test(expected = IllegalArgumentException.class)
+
+    @Test(expectedExceptions = { IllegalArgumentException.class })
     public void extendedBaseInterfaceTest() {
         @SuppressWarnings("unused")
         final MyConstructible m = OASFactory.createObject(MyConstructible.class);
     }
-    
-    @Test(expected = IllegalArgumentException.class)
+
+    @Test(expectedExceptions = { IllegalArgumentException.class })
     public void extendedInterfaceTest() {
         @SuppressWarnings("unused")
         final MyLicense m = OASFactory.createObject(MyLicense.class);
     }
-    
-    @Test(expected = IllegalArgumentException.class)
+
+    @Test(expectedExceptions = { IllegalArgumentException.class })
     public void customAbstractClassTest() {
         @SuppressWarnings("unused")
         final MyAbstractLicenseImpl m = OASFactory.createObject(MyAbstractLicenseImpl.class);
     }
-    
-    @Test(expected = IllegalArgumentException.class)
+
+    @Test(expectedExceptions = { IllegalArgumentException.class })
     public void customClassTest() {
         @SuppressWarnings("unused")
         final MyLicenseImpl m = OASFactory.createObject(MyLicenseImpl.class);
diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/PetStoreAppTest.java b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/PetStoreAppTest.java
new file mode 100644
index 0000000..a8989db
--- /dev/null
+++ b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/PetStoreAppTest.java
@@ -0,0 +1,123 @@
+/**
+ * Copyright (c) 2017 Contributors to the Eclipse Foundation
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.eclipse.microprofile.openapi.tck;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasEntry;
+import static org.hamcrest.Matchers.hasItems;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.nullValue;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.container.test.api.RunAsClient;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.testng.annotations.Test;
+
+import io.restassured.response.ValidatableResponse;
+
+public class PetStoreAppTest extends AppTestBase {
+    @Deployment(name = "petstore")
+    public static WebArchive createDeployment() {
+        return ShrinkWrap.create(WebArchive.class, "petstore.war")
+                .addPackages(true, "org.eclipse.microprofile.openapi.apps.petstore");
+    }
+
+    @RunAsClient
+    @Test(dataProvider = "formatProvider")
+    public void testSchema(String type) {
+        ValidatableResponse vr = callEndpoint(type);
+        // Schema and DiscriminatorMapping annotation tests
+        // Basic properties
+        vr.body("paths.'/pet/findByTags'.get.parameters.find{ it.name == 'tags' }.schema.default", equalTo("Dog"));
+        vr.body("paths.'/pet/findByTags'.get.parameters.find{ it.name == 'tags' }.schema.enum", hasItems("Cat", "Dog", "Lizard"));
+        vr.body("paths.'/pet/findByTags'.get.parameters.find{ it.name == 'tags' }.schema.externalDocs.description", equalTo("Pet Types"));
+        vr.body("paths.'/pet/findByTags'.get.parameters.find{ it.name == 'tags' }.schema.deprecated", equalTo(true));
+        vr.body("paths.'/store/order/{orderId}'.get.responses.'900'.schema", nullValue());
+
+        // Numerical properties
+        vr.body("paths.'/pet/{petId}'.get.parameters.find{ it.name == 'petId' }.schema.maximum", equalTo(101));
+        vr.body("paths.'/pet/{petId}'.get.parameters.find{ it.name == 'petId' }.schema.exclusiveMaximum", equalTo(true));
+        vr.body("paths.'/pet/{petId}'.get.parameters.find{ it.name == 'petId' }.schema.minimum", equalTo(9));
+        vr.body("paths.'/pet/{petId}'.get.parameters.find{ it.name == 'petId' }.schema.exclusiveMinimum", equalTo(true));
+        vr.body("paths.'/pet/{petId}'.get.parameters.find{ it.name == 'petId' }.schema.multipleOf", equalTo(10));
+
+        // String properties
+        vr.body("paths.'/pet/{petId}'.delete.parameters.find{ it.name == 'apiKey' }.schema.maxLength", equalTo(256));
+        vr.body("paths.'/pet/{petId}'.delete.parameters.find{ it.name == 'apiKey' }.schema.minLength", equalTo(32));
+
+        // Other properties
+        String storeOrderResponses = "paths.'/store/order/{orderId}'.get.responses";
+        vr.body("paths.'/pet/{petId}'.get.responses.'200'.content.'application/json'.schema.readonly", equalTo(true));
+        vr.body(storeOrderResponses + ".'404'.content.'application/json'.schema.anyOf",
+                hasItems("#/components/schemas/Order", "#/components/schemas/BadOrder"));
+        vr.body(storeOrderResponses + ".'200'.content.'application/json'.schema.allOf",
+                hasItems("#/components/schemas/Order", "#/components/schemas/Pet"));
+        vr.body("paths.'/pet/{petId}'.get.responses.'200'.content.'application/json'.schema.oneOf",
+                hasItems("#/components/schemas/Cat", "#/components/schemas/Dog", "#/components/schemas/Lizard"));
+        vr.body(storeOrderResponses + ".'200'.content.'application/json'.schema.not", equalTo("#/components/schemas/BadOrder"));
+        vr.body(storeOrderResponses + ".'404'.content.'application/json'.schema.discriminatorProperty", equalTo("id"));
+        vr.body(storeOrderResponses + ".'404'.content.'application/json'.schema.discriminatorMapping",
+                hasEntry("0", "#/components/schemas/BadOrder"));
+    }
+
+    @RunAsClient
+    @Test(dataProvider = "formatProvider")
+    public void testExtension(String type) {
+        // Extension annotation tests
+        ValidatableResponse vr = callEndpoint(type);
+
+        // extension on field
+        String componentExt = "components.schemas.User.extensions";
+        vr.body(componentExt, hasSize(3));
+        vr.body(componentExt + ".find{ it.name == 'x-mp-field1'}", equalTo(true));
+        vr.body(componentExt + ".find{ it.name == 'x-mp-field2'}", equalTo(true));
+        vr.body(componentExt + ".find{ it.name == 'x-mp-field3'}", equalTo(false));
+
+        // extension on method
+        String pathExt = "paths.'/pet/findByStatus'.get.extensions";
+        vr.body(pathExt, hasSize(3));
+        vr.body(pathExt + ".find{ it.name == 'x-mp-method1'}", equalTo(true));
+        vr.body(pathExt + ".find{ it.name == 'x-mp-method2'}", equalTo(true));
+        vr.body(pathExt + ".find{ it.name == 'x-mp-method3'}", equalTo(false));
+
+        // extension on parameter
+        String parmExt = "paths.'/pet/findByStatus'.parameters.find{ it.name == 'status' }.extensions";
+        vr.body(parmExt, hasSize(1));
+        vr.body(parmExt + ".find{ it.name == 'x-mp-parm1'}", equalTo(true));
+
+        // extension on type: app class
+        vr.body("extensions", hasSize(3));
+        vr.body("extensions.find{ it.name == 'x-mp-openapi1'}", equalTo(true));
+        vr.body("extensions.find{ it.name == 'x-mp-openapi2'}", equalTo(true));
+        vr.body("extensions.find{ it.name == 'x-mp-openapi3'}", equalTo(false));
+
+        // extension on type: resource class
+        endpointExtension(vr, "'/store/inventory'.get");
+        endpointExtension(vr, "'/store/order/{orderId}'.get");
+        endpointExtension(vr, "'/store/order'.post");
+        endpointExtension(vr, "'/store/order/{orderId}'.delete");
+    }
+
+    private void endpointExtension(ValidatableResponse vr, String endpoint) {
+        String ext1 = ".find{ it.name == 'x-mp-type1' }";
+        String ext2 = ".find{ it.name == 'x-mp-type2' }";
+        vr.body("paths." + endpoint + ".extensions", hasSize(2));
+        vr.body("paths." + endpoint + ".extensions" + ext1, equalTo(true));
+        vr.body("paths." + endpoint + ".extensions" + ext2, equalTo(false));
+    }
+}