Skip to content

REST Assured: Java API Testing Framework for RESTful Services

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about REST Assured: Java API Testing Framework for RESTful Services. We cover key concepts, practical examples, and best practices to help you master this topic.

REST Assured is a Java DSL for testing RESTful APIs that provides a given/when/then syntax, built-in JSON and XML validation, authentication support, serialization, and seamless Spring Boot testing integration.

What You'll Learn

How to test Java REST APIs with REST Assured, use given/when/then syntax, validate JSON responses with Hamcrest matchers, authenticate with OAuth2 and Basic auth, use specifications for DRY configuration, and integrate with Spring Boot tests.

Why It Matters

REST Assured is the standard Java API Testing Library with 5M+ weekly downloads. Its expressive DSL makes tests readable and maintainable. DodaTech uses REST Assured for all Java microservice integration tests.

Real-World Use

A DodaTech Java developer writes a REST Assured test for the payment API: given a valid auth token and payment payload, when POSTing to /payments, then expect 200 with a Hamcrest matcher verifying the payment ID format.

flowchart LR
    A["given()"] --> B["Request\nSpecification"]
    B --> C["Headers,\nAuth, Body"]
    C --> D["when()"]
    D --> E["HTTP Method\nand Path"]
    E --> F["then()"]
    F --> G["Assertions"]
    G --> H["Status,\nBody, Headers"]
    style A fill:#dbeafe,stroke:#2563eb
    style E fill:#bbf7d0,stroke:#16a34a
    style G fill:#fef3c7,stroke:#d97706

Basic REST Assured Test

import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

public class UserApiTest {

    @Test
    public void testGetUsersReturnsList() {
        given()
            .baseUri("https://api.dodatech.com/v1")
        .when()
            .get("/users")
        .then()
            .statusCode(200)
            .contentType(ContentType.JSON)
            .body("size()", greaterThan(0))
            .body("[0].email", containsString("@"));
    }

    @Test
    public void testCreateUser() {
        String requestBody = """
            {
                "email": "rest-assured@test.com",
                "name": "REST Assured User",
                "password": "SecurePass123!"
            }
        """;

        given()
            .baseUri("https://api.dodatech.com/v1")
            .contentType(ContentType.JSON)
            .body(requestBody)
        .when()
            .post("/users")
        .then()
            .statusCode(201)
            .body("id", notNullValue())
            .body("email", equalTo("rest-assured@test.com"));
    }
}

// Expected output:
// testGetUsersReturnsList PASSED
// testCreateUser PASSED

Authentication Tests

import io.restassured.RestAssured;
import io.restassured.authentication.OAuth2Scheme;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.specification.RequestSpecification;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

public class AuthApiTest {

    private static RequestSpecification authenticatedSpec;

    @BeforeAll
    public static void setup() {
        // Obtain token via login
        String token = given()
            .contentType(ContentType.JSON)
            .body("{\"email\":\"admin@test.com\",\"password\":\"admin123\"}")
        .when()
            .post("/auth/login")
        .then()
            .extract()
            .path("token");

        // Create reusable authenticated spec
        authenticatedSpec = new RequestSpecBuilder()
            .setBaseUri("https://api.dodatech.com/v1")
            .setContentType(ContentType.JSON)
            .addHeader("Authorization", "Bearer " + token)
            .build();
    }

    @Test
    public void testAuthenticatedEndpoint() {
        given()
            .spec(authenticatedSpec)
        .when()
            .get("/admin/users")
        .then()
            .statusCode(200);
    }

    @Test
    public void testUnauthenticatedReturns401() {
        given()
            .baseUri("https://api.dodatech.com/v1")
        .when()
            .get("/admin/users")
        .then()
            .statusCode(401);
    }

    @Test
    public void testOAuth2Token() {
        given()
            .baseUri("https://api.dodatech.com/v1")
            .auth().oauth2("eyJhbGciOiJIUzI1NiJ9...")
        .when()
            .get("/protected/resource")
        .then()
            .statusCode(200);
    }
}

JSON Response Validation

import static org.hamcrest.Matchers.*;
import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath;

public class ResponseValidationTest {

    @Test
    public void testNestedJsonValidation() {
        given()
            .baseUri("https://api.dodatech.com/v1")
            .pathParam("userId", 42)
        .when()
            .get("/users/{userId}")
        .then()
            .body("id", equalTo(42))
            .body("email", containsString("@"))
            .body("profile.name", notNullValue())
            .body("profile.avatar_url", startsWith("https://"))
            .body("roles", hasItem("user"))
            .body("roles.size()", greaterThanOrEqualTo(1));
    }

    @Test
    public void testJsonSchemaValidation() {
        given()
            .baseUri("https://api.dodatech.com/v1")
        .when()
            .get("/users/42")
        .then()
            .body(matchesJsonSchemaInClasspath("schemas/user-schema.json"));
    }

    @Test
    public void testListWithFilters() {
        given()
            .baseUri("https://api.dodatech.com/v1")
            .queryParam("role", "admin")
            .queryParam("status", "active")
        .when()
            .get("/users")
        .then()
            .body("findAll { it.role == 'admin' }.size()", greaterThan(0))
            .body("findAll { it.status == 'inactive' }.size()", equalTo(0));
    }
}

Spring Boot Integration

import io.restassured.RestAssured;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserControllerTest {

    @LocalServerPort
    private int port;

    @BeforeEach
    void setUp() {
        RestAssured.port = port;
        RestAssured.baseURI = "http://localhost";
    }

    @Test
    void testCreateUserWithSpringBoot() {
        given()
            .contentType(ContentType.JSON)
            .body("""
                {
                    "email": "spring-test@example.com",
                    "name": "Spring Boot Test",
                    "password": "Test123!"
                }
            """)
        .when()
            .post("/api/users")
        .then()
            .statusCode(201)
            .body("email", equalTo("spring-test@example.com"));
    }

    @Test
    void testGetUserNotFound() {
        given()
        .when()
            .get("/api/users/99999")
        .then()
            .statusCode(404)
            .body("error", containsString("not found"));
    }
}

Common Mistakes

1. Not Extracting Tokens for Reuse

Calling login API before every test is slow. Extract the token in @BeforeClass/@BeforeAll and store in a static RequestSpecification for reuse across tests.

2. Hardcoding Request Bodies

String literals in Java code are hard to maintain. Use Java records/POJOs with JSON serialization (Jackson/Gson) or read JSON templates from classpath files.

3. Forgetting contentType Specification

REST Assured does not default to JSON. Always set .contentType(ContentType.JSON) or define it in a RequestSpecBuilder to avoid 415 Unsupported Media Type errors.

4. Not Using Path Parameters

Hardcoding IDs in URLs creates brittle tests. Use .pathParam("id", value) and /{id} in the URL template for readability and maintainability.

5. Ignoring Response Extraction

Use .extract().path("field") or .extract().response() to capture dynamic values from responses for use in subsequent test steps.

Practice Questions

  1. What is the given/when/then syntax in REST Assured?
  2. How do you extract a value from a response for use in another request?
  3. How do you validate a JSON response against a schema?
  4. How does REST Assured integrate with Spring Boot tests?

Answers:

  1. given() sets up request (headers, auth, body, params). when() specifies the HTTP method and endpoint. then() contains assertions on the response (status, body, headers).
  2. Use .extract().path("field.name") to extract a single value, or .extract().response() to get the full response object for multiple extractions.
  3. Add io.rest-assured:json-schema-validator dependency. Use .body(matchesJsonSchemaInClasspath("schema.json")) to validate against a JSON schema file in the classpath.
  4. Use @SpringBootTest with RANDOM_PORT, inject @LocalServerPort for the port, set RestAssured.port in @BeforeEach, and use REST Assured normally against the running Spring Boot context.

Challenge: Write a complete REST Assured test suite for a Spring Boot REST API: create a RequestSpecification for auth, test CRUD endpoints with JSON validation, use JSON schema validation for all responses, extract IDs between requests, test error cases (400, 401, 404, 500), and run as part of Maven build.

FAQ

{{< faq "Can REST Assured test XML APIs?" "Yes, REST Assured has built-in XML support using body(hasXPath("//element")) for XPath assertions and body(xmlPath("root.element").toString()) for XML path extraction." >}}

How do I reuse configuration across tests?

Use RequestSpecBuilder to create a reusable RequestSpecification. Set base URI, auth, content type, headers, and filters. Use .spec(mySpec) in each test.

How do I log requests and responses for debugging?

Use .log().all() before the request and .log().all() after the response. For selective logging: .log().method(), .log().body(), .log().headers(), .log().cookies().

What is the difference between `body` and `param`?

body() sets the request body (JSON/XML). param() sets query/form parameters. For file uploads, use multiPart().

{{< faq "Can REST Assured handle multipart file uploads?" "Yes, use .multiPart(new File(\"path/to/file\"), \"image/png\") or .multiPart(\"field\", \"content\", \"text/plain\") for text parts." >}}

Mini Project

Write a REST Assured test suite for a Spring Boot blog API: RequestSpecification for auth, CRUD tests for posts and comments, JSON schema validation, parameterized tests for validation errors, file upload for post images, pagination testing, and Maven Surefire/Jacoco coverage reporting.

What's Next

HTTP Client — use Python httpx and aiohttp for async HTTP testing.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro