Rest Assured — Step-by-step guide

From zero to full reference: Maven setup, client layer, JSON schemas, security, OpenAPI, and CI — 98 tests.

API · Java 17 · Spring Petclinic REST

What is Rest Assured?

Rest Assured is a Java DSL for testing REST APIs. It integrates with JUnit 5, Hamcrest matchers, Jackson POJO mapping, and Allure reporting — ideal for contract and integration tests.

  • BDD-style syntax: given() · when() · then()
  • JsonPath extraction without full object mapping
  • JSON schema validation via Rest Assured JSON Schema Validator
  • Filters for logging, SLA checks, and custom network capture

Prerequisites

  • Java 17+
  • Docker (recommended)
  • Maven 3.9+
java -version
mvn -version
docker --version

Installation & first run

git clone https://github.com/lflucasferreira/rest-assured.git
cd rest-assured

# Option A — Docker (default API + secure API + tests)
docker compose up --abort-on-container-exit --exit-code-from tests

# Option B — local Maven (start API first)
docker run -p 9966:9966 springcommunity/spring-petclinic-rest:latest
docker run -p 9967:9966 -e PETCLINIC_SECURITY_ENABLE=true springcommunity/spring-petclinic-rest:latest
mvn clean test

Project structure

src/test/java/com/portfolio/petclinic/
├── base/BaseTest.java
├── clients/                    # Actuator, Owners, Pets, Visits, Vets, ...
├── models/                     # POJOs + User, Vet, Specialty
├── tests/
│   ├── smoke/                  # ActuatorSmokeTest
│   ├── security/               # Basic Auth, RBAC, Users
│   ├── advanced/               # WireMock, OpenAPI, performance
│   ├── flows/                  # PetLifecycleFlowTest
│   └── negative/               # Parameterized error paths
└── utils/                      # Config, factories, validators

src/test/resources/
├── config/dev.properties       # API 9966 + secure 9967
├── config/docker.properties
└── schemas/*.json              # owner, pet, visit, vet, openapi, ...

Suite coverage (98 tests)

Summary of packages and clients added in the expanded suite:

AreaTest classesMain endpoints
OwnersOwnersApiTest, OwnersNegativeApiTest, OwnersV2ApiTestGET/POST/PUT/DELETE /owners, pagination /v2/owners
PetsPetsApiTest, PetsNestedApiTest, PetsNegativeApiTest/pets, /owners/{id}/pets
VisitsVisitsApiTest, VisitsNegativeApiTestCRUD /visits, nested POST
Vets & SpecialtiesVetsApiTest, SpecialtiesApiTestCRUD /vets, /specialties
Pet typesPetTypesApiTestCRUD /pettypes
SmokeActuatorSmokeTest/actuator/health
SecuritySecurityApiTest, SecurityAuthorizationTest, UsersApiTestBasic Auth on API :9967
AdvancedOpenApiContractTest, WireMockResilienceTest, PerformanceSmokeTest, …OpenAPI, SLA, integrity

API client layer

All HTTP calls go through client classes. See api-testing-strategy.md.

public class OwnersClient extends ApiClient {
    public Response getAllOwners() {
        return given().spec(requestSpec).when().get("/owners");
    }
    public Response createOwner(Owner owner) {
        return given().spec(requestSpec).body(owner).when().post("/owners");
    }
}

BaseTest setup

@BeforeEach
void setUp() {
    RestAssured.requestSpecification = requestSpec;
    networkCapture.reset();
}

BaseTest wires OwnersClient, PetsClient, PetTypesClient, VisitsClient, and the network capture filter.

Assertions & JSON schemas

ResponseValidator.assertStatusCode(response.getStatusCode(), 201);
ResponseValidator.assertMatchesSchema(body, "schemas/owner-schema.json");

Owner owner = response.as(Owner.class);
assertThat(owner.getFirstName(), is(payload.getFirstName()));

Test data factory

Owner owner = TestDataFactory.buildOwner();
PetFields pet = TestDataFactory.buildPetFields(typeId, typeName);
Owner invalid = TestDataFactory.buildInvalidOwnerWithAlphabeticTelephone();

JavaFaker generates unique data; negative factories encode invalid payloads.

Network capture

NetworkInspector.attachLastExchangeToAllure(networkCapture);
NetworkInspector.assertLastRequestUriContains(networkCapture, "/pets");
NetworkInspector.assertResponseSequenceContains(networkCapture, "/owners", "/pets/");

WireMock

wireMockSupport.server().stubFor(post(urlEqualTo("/audit/owner-created"))
    .willReturn(aResponse().withStatus(202)));

WireMock.verify(postRequestedFor(urlEqualTo("/audit/owner-created"))
    .withRequestBody(equalToJson(expectedJson, true, true)));

Additional scenarios in WireMockResilienceTest (timeout, invalid JSON, connection reset) and WireMockRetryIntegrationTest (retry after 503).

Security & RBAC

Docker Compose runs two API instances:

  • 9966 — no authentication (default)
  • 9967 — PETCLINIC_SECURITY_ENABLE=true, Basic Auth admin:admin
OwnersClient.secured().getAllOwners();
OwnersClient.withCredentials("admin", "admin").createOwner(payload);
UsersClient.secured().createUser(TestDataFactory.buildUniqueSecureUser("OWNER_ADMIN"));

SecurityApiTest validates 401/200. SecurityAuthorizationTest exercises RBAC (VET_ADMIN vs OWNER_ADMIN) when provisioned-user login is available.

OpenAPI & contracts

openApiClient.getApiDocs(); // GET /v3/api-docs

OpenApiResponseValidator.assertResponseMatchesDocumentedOperation(
    openApiResponse, apiResponse, "/api/owners", "get");

Schemas: openapi-docs-schema.json, visit-schema.json, vet-schema.json, specialty-schema.json, owners-page-schema.json.

JUnit tags

mvn test -Dtest.groups=smoke        # Actuator
mvn test -Dtest.groups=security     # Secure API
mvn test -Dtest.groups=performance  # Response-time SLA
mvn test -Dtest.groups=contract     # OpenAPI per operation

Parameterized tests

@ParameterizedTest(name = "GET /owners/{0} should return {1}")
@CsvSource({ "99999, 404", "0, 404" })
void shouldReturnExpectedStatusForInvalidOwnerIds(int ownerId, int expectedStatus) {
    Response response = ownersClient.getOwnerById(ownerId);
    ErrorResponseValidator.assertErrorStatusAndOptionalProblemDetail(...);
}

How to run

mvn clean test
mvn test -Dtest=VisitsApiTest
mvn test -Denv=dev -Dapi.base.uri=http://localhost:9966/petclinic/api \
  -Dapi.secure.base.uri=http://localhost:9967/petclinic/api
mvn test -Dtest.groups=smoke
mvn allure:serve

CI — GitHub Actions

Workflow .github/workflows/api-tests.yml:

  • Two service containers: default API (9966) and secure API (9967)
  • Health check on Actuator /actuator/health
  • mvn clean test — 98 tests, gate on every PR/push to main
  • Allure report published to GitHub Pages on push to main

Training documentation