TestingAPIs is still being built. If something breaks, email support@testingapis.com.

given, when, then: your first tests

How a REST Assured test reads, what each part does, and what a failure tells you.

A REST Assured test reads in three parts. given() describes the request, when() sends it, and then()lists what must be true of the response. The words come from behaviour-driven development, but you don't need to know anything about that: they're just a readable way to chain method calls. This lesson goes through the first test class line by line.

The first test class

This is FirstTest.java from the example project, exactly as it runs:

FirstTest.java
package com.testingapis.examples;

import io.restassured.http.ContentType;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;

/** Lesson 2: the given / when / then shape, with nothing shared yet. */
class FirstTest {

    @Test
    @Tag("smoke")
    void productOneHasATitleAndAPrice() {
        given()
            .baseUri("https://api.testingapis.com")
        .when()
            .get("/api/Products/1")
        .then()
            .statusCode(200)
            .contentType(ContentType.JSON)
            .body("id", equalTo(1))
            .body("title", equalTo("Wireless Noise-Cancelling Headphones"))
            .body("price", greaterThan(0f));
    }

    @Test
    void aMissingProductReturns404() {
        given()
            .baseUri("https://api.testingapis.com")
        .when()
            .get("/api/Products/9999")
        .then()
            .statusCode(404)
            .body("message", containsString("9999"));
    }
}

The imports

REST Assured is used through static imports, so given() can be written without a class name. The matchers, equalTo, greaterThan and containsString, come from Hamcrest, which REST Assured includes. Your editor will usually offer to add these imports; when it doesn't, it's nearly always because a static import is missing.

The three parts

PartHereTypical contents
given().baseUri("https://api.testingapis.com")Base URL, headers, query and path parameters, auth, body
when().get("/api/Products/1")The method and path: get, post, put, patch, delete
then().statusCode(200) … .body(…)Status, headers, content type and body checks

when() and then()don't do anything themselves; they exist to make the test read well. The request is sent by .get(...). The indentation, with given, when and thenat one level and their details below, isn't required either, but it's the convention, and it makes long tests far easier to scan.

Checks on the body

.body(path, matcher) reaches into the JSON response by path and applies a Hamcrest matcher. "id" is the top-level id field; lesson 3 covers nested fields and lists. Each check is independent, so a test can hold several:

java
.then()
    .statusCode(200)
    .body("id", equalTo(1))
    .body("title", equalTo("Wireless Noise-Cancelling Headphones"))

Each .body()call is checked as it's reached, so the first one that fails ends the test and you never hear about the rest. When you want every mismatch at once, pass several path and matcher pairs to a single .body(). Both versions against product 1, with two deliberately wrong expectations:

Checks and their output
// Separate .body() calls: the run stops at the first one that fails
.body("id", equalTo(2))
.body("category", equalTo("books"))

1 expectation failed.
JSON path id doesn't match.
Expected: <2>
  Actual: <1>

// The same two checks in one .body() call: both are reported
.body("id", equalTo(2), "category", equalTo("books"))

2 expectations failed.
JSON path id doesn't match.
Expected: <2>
  Actual: <1>

JSON path category doesn't match.
Expected: books
  Actual: electronics

The single call reported both problems in one run. For a few checks the difference hardly matters; for a test that compares ten fields of a record, seeing every wrong field at once saves several rounds of fix, run, fix again.

Testing a failure on purpose

The second test asks for a product that doesn't exist and expects a 404 with the ID in the message. REST Assured doesn't treat a 4xx or 5xx response as an error: it's just a response, and your statusCodecheck decides what's right. That's what makes negative tests this short.

Reading a failure

Here is a real failing check, from a test that expected the health endpoint to say up:

Terminal
[ERROR] com.testingapis.examples.SpecsTest.healthCheck -- Time elapsed: 3.747 s <<< FAILURE!
java.lang.AssertionError:
1 expectation failed.
JSON path status doesn't match.
Expected: up
  Actual: healthy

The path that failed, what was expected and what came back. The example project also turns on REST Assured's logging for failed tests only, so the same output includes the whole request and response:

Terminal
Request method:	GET
Request URI:	https://api.testingapis.com/api/health
Headers:		Accept=application/json, application/javascript, text/javascript, text/json
Body:			<none>

HTTP/1.1 200 OK
content-type: application/json; charset=utf-8
...

{
    "status": "healthy",
    "uptimeSeconds": 84939,
    "timestamp": "2026-10-11T14:01:55Z"
}

That's usually enough to see the problem without rerunning anything. Lesson 6 shows where the setting lives. Notice the Accept header too: asking for ContentType.JSON sends four media types, not just application/json. Most APIs don't mind; one that checks Accept strictly might.

Habits from the start

  • Check the status first, then the content type, then the body.
  • Name test methods as sentences about the API, like aMissingProductReturns404.
  • Write at least one negative test for every positive one.
  • Keep one request per test unless the test is about a sequence, like the chains in lesson 4.

Exercise

Add a test to FirstTest for GET /api/Todos/1 that checks the status, that the content type is JSON, and that completed is a boolean. You'll need a matcher for the type; Hamcrest's instanceOf(Boolean.class) is one option. Then make each check fail in turn and read the message REST Assured gives.

Practise and go further