What Is REST Assured?

REST Assured is a Java library that simplifies testing RESTful APIs. It provides a domain-specific language (DSL) built on a Given-When-Then pattern that makes API tests read like natural language specifications. Instead of manually constructing HTTP requests, parsing responses, and writing complex assertions, REST Assured handles these operations with a fluent, chainable API.

REST Assured is the most popular API testing library in the Java ecosystem, used by thousands of organizations for automated API validation. It integrates seamlessly with JUnit, TestNG, Maven, Gradle, and CI/CD pipelines.

Project Setup

Maven Configuration

<!-- pom.xml -->
<dependencies>
    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>rest-assured</artifactId>
        <version>5.4.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>json-schema-validator</artifactId>
        <version>5.4.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.10.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.hamcrest</groupId>
        <artifactId>hamcrest</artifactId>
        <version>2.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Static Imports

import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

The Given-When-Then Pattern

Every REST Assured test follows three phases:

given()    // Set up the request: headers, body, params, auth
  .header("Content-Type", "application/json")
  .body(requestBody)
.when()    // Execute the HTTP method
  .post("/api/users")
.then()    // Validate the response
  .statusCode(201)
  .body("name", equalTo("Alice"));

CRUD Operations

GET Request

@Test
void shouldReturnAllUsers() {
    given()
        .baseUri("https://api.example.com")
    .when()
        .get("/api/users")
    .then()
        .statusCode(200)
        .body("size()", greaterThan(0))
        .body("[0].name", notNullValue())
        .body("findAll { it.active == true }.size()", greaterThan(0));
}

@Test
void shouldReturnUserById() {
    given()
        .baseUri("https://api.example.com")
        .pathParam("id", 42)
    .when()
        .get("/api/users/{id}")
    .then()
        .statusCode(200)
        .body("id", equalTo(42))
        .body("name", equalTo("Alice"))
        .body("email", matchesPattern(".*@.*\\..*"));
}

POST Request

@Test
void shouldCreateNewUser() {
    String requestBody = """
        {
            "name": "Bob Johnson",
            "email": "bob@example.com",
            "role": "developer"
        }
        """;

    given()
        .baseUri("https://api.example.com")
        .contentType(ContentType.JSON)
        .body(requestBody)
    .when()
        .post("/api/users")
    .then()
        .statusCode(201)
        .body("id", notNullValue())
        .body("name", equalTo("Bob Johnson"))
        .header("Location", containsString("/api/users/"));
}

PUT and DELETE

@Test
void shouldUpdateUser() {
    String updateBody = """
        { "name": "Alice Updated", "role": "lead" }
        """;

    given()
        .contentType(ContentType.JSON)
        .body(updateBody)
    .when()
        .put("/api/users/42")
    .then()
        .statusCode(200)
        .body("name", equalTo("Alice Updated"))
        .body("role", equalTo("lead"));
}

@Test
void shouldDeleteUser() {
    when()
        .delete("/api/users/42")
    .then()
        .statusCode(204);

    // Verify deletion
    when()
        .get("/api/users/42")
    .then()
        .statusCode(404);
}

Query Parameters and Headers

@Test
void shouldFilterUsersWithQueryParams() {
    given()
        .queryParam("role", "admin")
        .queryParam("active", true)
        .queryParam("page", 1)
        .queryParam("limit", 10)
    .when()
        .get("/api/users")
    .then()
        .statusCode(200)
        .body("every { it.role == 'admin' }", is(true));
}

Response Extraction

@Test
void shouldExtractAndUseResponseData() {
    // Create a user and extract the ID
    int userId = given()
        .contentType(ContentType.JSON)
        .body("{ \"name\": \"Test User\", \"email\": \"test@example.com\" }")
    .when()
        .post("/api/users")
    .then()
        .statusCode(201)
        .extract()
        .path("id");

    // Use the extracted ID in a follow-up request
    given()
        .pathParam("id", userId)
    .when()
        .get("/api/users/{id}")
    .then()
        .statusCode(200)
        .body("name", equalTo("Test User"));
}

// Extract entire response as object
Response response = given()
    .when()
    .get("/api/users/1")
    .then()
    .extract()
    .response();

String name = response.jsonPath().getString("name");
List<String> roles = response.jsonPath().getList("roles");

Authentication

// Basic Auth
given()
    .auth().basic("username", "password")
.when()
    .get("/api/protected");

// Bearer Token
given()
    .header("Authorization", "Bearer " + token)
.when()
    .get("/api/protected");

// OAuth 2.0
given()
    .auth().oauth2(accessToken)
.when()
    .get("/api/protected");

JSON Schema Validation

// src/test/resources/schemas/user-schema.json
@Test
void shouldMatchUserSchema() {
    given()
    .when()
        .get("/api/users/1")
    .then()
        .statusCode(200)
        .body(matchesJsonSchemaInClasspath("schemas/user-schema.json"));
}

Reusable Specifications

public class ApiSpecs {
    public static RequestSpecification baseSpec() {
        return new RequestSpecBuilder()
            .setBaseUri("https://api.example.com")
            .setContentType(ContentType.JSON)
            .addHeader("X-API-Version", "2")
            .build();
    }

    public static RequestSpecification authSpec(String token) {
        return new RequestSpecBuilder()
            .addRequestSpecification(baseSpec())
            .addHeader("Authorization", "Bearer " + token)
            .build();
    }

    public static ResponseSpecification successSpec() {
        return new ResponseSpecBuilder()
            .expectStatusCode(200)
            .expectContentType(ContentType.JSON)
            .expectResponseTime(lessThan(3000L))
            .build();
    }
}

// Usage in tests
@Test
void shouldGetUserWithSpecs() {
    given()
        .spec(ApiSpecs.authSpec(token))
    .when()
        .get("/api/users/1")
    .then()
        .spec(ApiSpecs.successSpec())
        .body("name", notNullValue());
}

Logging for Debugging

given()
    .log().all()          // Log full request
.when()
    .get("/api/users")
.then()
    .log().ifError()      // Log response only on failure
    .statusCode(200);

// Other logging options
.log().body()             // Log body only
.log().headers()          // Log headers only
.log().ifValidationFails() // Log only when assertions fail

Exercises

Exercise 1: CRUD Test Suite

Build a complete CRUD test suite for a REST API:

  1. POST — Create a new resource, extract its ID
  2. GET — Retrieve the resource by ID, validate all fields
  3. PUT — Update specific fields, verify changes
  4. DELETE — Remove the resource, verify 404 on subsequent GET
  5. Use reusable RequestSpecification for base URL and auth

Exercise 2: Schema Validation

  1. Create a JSON schema file for a user response object
  2. Write a test that validates the schema for single user and user list endpoints
  3. Modify the schema to make a field required, and verify the test catches a violation
  4. Add schema validation to your CRUD test suite from Exercise 1

Exercise 3: Authentication Flow

  1. Write a test that authenticates via POST /auth/login and extracts a JWT token
  2. Use the extracted token for subsequent authenticated requests
  3. Test that unauthenticated requests return 401
  4. Test that expired tokens return 403 with an appropriate error message