REST Assured (Java)

24REST Assured (Java)

What you will master here

  • What REST Assured is and why teams use it
  • The Given–When–Then BDD syntax
  • Validating status, headers, body with JSONPath
  • Auth (basic, bearer, OAuth2)
  • Serialization with POJOs (Jackson)
  • How REST Assured compares to Playwright's request API

24.1 What REST Assured is

REST Assured is a Java DSL (domain-specific language) for testing HTTP APIs. It is the most popular API testing library in Java/JUnit/TestNG shops. It wraps Apache HTTP Client and adds a readable, fluent BDD-style assertion syntax.

Why teams use it:

24.2 Setup (Maven)

// pom.xml dependency
<dependency>
  <groupId>io.rest-assured</groupId>
  <artifactId>rest-assured</artifactId>
  <version>5.5.0</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.testng</groupId>
  <artifactId>testng</artifactId>
  <version>7.10.2</version>
  <scope>test</scope>
</dependency>
// Static imports — make tests readable
import static io.restassured.RestAssured.*;
import static io.restassured.matcher.RestAssuredMatchers.*;
import static org.hamcrest.Matchers.*;

24.3 The Given–When–Then syntax

REST Assured follows BDD style:

@Test
public void getUserReturns200() {
  given()
    .baseUri("https://api.example.com")
    .header("Accept", "application/json")
  .when()
    .get("/users/42")
  .then()
    .statusCode(200)
    .contentType("application/json")
    .body("id", equalTo(42))
    .body("email", equalTo("alice@test.com"))
    .body("roles", hasItem("admin"))
    .time(lessThan(500L));
}
Read it out loud"Given a base URI and Accept header, when I GET /users/42, then status code is 200, body.id is 42, body.email is alice@test.com, body.roles has admin, response time is < 500 ms."

24.4 POST with a JSON body

@Test
public void createUser() {
  String payload = "{\"name\":\"Alice\",\"email\":\"alice@test.com\"}";
  given()
    .baseUri("https://api.example.com")
    .contentType("application/json")
    .body(payload)
  .when()
    .post("/users")
  .then()
    .statusCode(201)
    .body("id", notNullValue())
    .header("Location", containsString("/users/"));
}

24.5 JSONPath — extracting and asserting

REST Assured uses Groovy GPath syntax (basically JSONPath with dots):

// Body: { "orders": [ { "id":1, "total": 99 }, { "id":2, "total": 49 } ] }

given().when().get("/orders").then()
  .body("orders.size()", equalTo(2))
  .body("orders[0].id", equalTo(1))
  .body("orders.total", hasItems(99, 49))
  .body("orders.findAll { it.total > 50 }.size()", equalTo(1))   // groovy filter
  .body("orders.total.sum()", equalTo(148));

// Extract a value
int orderId = given().when().get("/orders").then()
  .extract().path("orders[0].id");

// Extract whole response
Response res = given().when().get("/orders");
List<Integer> totals = res.jsonPath().getList("orders.total");

24.6 Auth patterns

// Basic
given().auth().basic("user", "pass").when().get("/secure").then().statusCode(200);

// Bearer
given().auth().oauth2(token).when().get("/me").then().statusCode(200);

// API key as header
given().header("x-api-key", "abc123").when().get("/v1/data").then().statusCode(200);

// OAuth2 password grant — fetch token, then use it
String token = given()
    .contentType("application/x-www-form-urlencoded")
    .formParam("grant_type", "password")
    .formParam("username", "qa")
    .formParam("password", "qa-pass")
  .when().post("/oauth/token")
  .then().extract().path("access_token");

24.7 Serialization with POJOs (Jackson)

Rather than building JSON strings by hand, define a Java class and let Jackson serialise it:

public class User {
  public String name;
  public String email;
  public User(String name, String email) { this.name = name; this.email = email; }
}

@Test
public void createUserWithPojo() {
  User payload = new User("Alice", "alice@test.com");
  User created = given()
      .contentType("application/json")
      .body(payload)
    .when().post("/users")
    .then().statusCode(201)
    .extract().as(User.class);     // deserialise response

  assertEquals(created.name, "Alice");
}

24.8 Reusable request specs

// Define once
public class Specs {
  public static RequestSpecification authed() {
    return new RequestSpecBuilder()
      .setBaseUri("https://api.example.com")
      .setContentType("application/json")
      .addHeader("Authorization", "Bearer " + System.getenv("TOKEN"))
      .build();
  }
}

// Use in every test
given().spec(Specs.authed()).when().get("/me").then().statusCode(200);

24.9 Logging

// Log everything (great for debugging a failing test)
given().log().all()
  .when().get("/users/42")
  .then().log().ifValidationFails()
  .statusCode(200);

// Common config: log on failure only
RestAssured.filters(
  new ErrorLoggingFilter(),
  new ResponseLoggingFilter(LogDetail.STATUS)
);

24.10 Negative cases

@Test public void missingEmailReturns400() {
  given().contentType("application/json").body("{\"name\":\"A\"}")
    .when().post("/users")
    .then().statusCode(400)
    .body("errors.email", equalTo("required"));
}

@Test public void wrongTokenReturns401() {
  given().auth().oauth2("invalid").when().get("/me").then().statusCode(401);
}

@Test public void userTriesAdminReturns403() {
  given().auth().oauth2(userToken).when().delete("/admin/items/1").then().statusCode(403);
}

24.11 REST Assured vs Playwright API — honest comparison

AspectREST AssuredPlaywright request
LanguageJava (also Kotlin/Scala)JS/TS/Python/Java/.NET
StyleBDD Given/When/Then chainimperative async/await
JSONPath/GPathFirst-class, terse assertsPlain JS object navigation
Schema validationJSON Schema validator out of the boxExternal (Zod/Ajv)
UI tests in same projectNo (needs Selenium/another tool)Yes — same runner does both
Hybrid (API setup + UI)Multi-tool integrationSingle fixture, dead simple
ReportersAllure, ExtentReports (mature)HTML, JUnit, Allure plugin
Best when…Java backend team writes API tests next to service codeJS/TS team wants one tool for API + UI + visual + perf-smoke
The pragmatic takeIf your application is Java and the API tests live with the service code, REST Assured is excellent. If you're an SDET writing end-to-end suites and UI tests, Playwright's request fixture is enough — and you avoid running two test frameworks.

24.12 Side-by-side: same test in both

REST Assured

@Test public void getOrders() {
  given().auth().oauth2(token)
    .when().get("/api/orders")
    .then().statusCode(200)
      .body("size()", greaterThan(0))
      .body("[0].total", greaterThan(0f));
}

Playwright

test('GET /api/orders', async ({ request }) => {
  const res = await request.get('/api/orders', {
    headers: { Authorization: `Bearer ${token}` },
  });
  expect(res.status()).toBe(200);
  const body = await res.json();
  expect(body.length).toBeGreaterThan(0);
  expect(body[0].total).toBeGreaterThan(0);
});

Module 6 — Interview Q&A bank

What is REST Assured?
A Java DSL for testing REST APIs. Wraps Apache HTTP Client and provides a fluent, BDD-style API (given/when/then) plus first-class JSONPath/GPath assertions. The dominant choice in Java/JUnit/TestNG shops.
Explain the given/when/then pattern.
given() configures the request (headers, params, body, auth). when() invokes the HTTP verb (get/post/put/delete). then() asserts on the response (status, body, headers, time). It reads like English and forces a clear test structure.
How do you assert a JSON field value?
In the then() chain: .body("path.to.field", equalTo(value)) using Hamcrest matchers. For arrays, use .body("items.id", hasItems(1,2)) or GPath filters like "items.findAll { it.total > 50 }.size()".
How do you extract a value to use in a later test?
String token = given()...when()...then().extract().path("access_token");. Or for the full response: Response r = given().when().get("/x"); r.jsonPath().getList("orders.total");
How do you send a POJO as a request body?
Configure .contentType("application/json") and pass the object: .body(myPojo). REST Assured uses Jackson to serialise. To deserialise the response: .extract().as(MyClass.class).
How do you reuse request configuration?
Build a RequestSpecification via RequestSpecBuilder (base URI, headers, auth, content type), then attach it: given().spec(mySpec). Similarly ResponseSpecBuilder for common assertions.
How do you authenticate?
.auth().basic(u,p), .auth().oauth2(token), or just .header("Authorization", "Bearer "+token). For OAuth2 password grant, hit the token endpoint first and reuse the token.
What's a common pitfall in REST Assured tests?
Forgetting to set the content type when sending a body — the server then can't parse it. Always pair .contentType("application/json") with .body(...).
How do you validate JSON schema?
REST Assured has a built-in JSON Schema validator: .body(matchesJsonSchemaInClasspath("schemas/order.json")). The schema file lives in src/test/resources/schemas/.
When would you pick REST Assured over Playwright for API testing?
When the service under test is in Java and the team wants API tests in the same language and build (Maven/Gradle) as the service — so models, fixtures and CI pipelines are shared. Also when teams already have heavy TestNG/Allure infrastructure.
When would you NOT pick REST Assured?
When you also need to write UI / E2E tests and don't want two frameworks. Playwright's request handles API and UI in one tool, with shared fixtures, parallelism, reports and traces.
How does REST Assured handle response time assertions?
.time(lessThan(500L)) — Hamcrest matcher on response time in ms. Useful for smoke-level SLA checks (real load testing still needs k6/Gatling).
How do you log request/response only on failure?
.then().log().ifValidationFails() on the request, or globally via filters: RestAssured.filters(new ErrorLoggingFilter()).
Difference between body() for assertion vs body() for request?
On given(), .body(payload) sets the request body. On then(), .body(path, matcher) asserts on a JSON path in the response body. Same name, different role based on context.
How would you parameterise a REST Assured test with TestNG?
Use @DataProvider in TestNG to feed parameter combinations (e.g. different IDs / payloads) into the test method, and assert per case.
Can REST Assured handle XML?
Yes — XMLPath works analogously to JSONPath, and .contentType("application/xml") serializes JAXB-annotated objects. Hamcrest matchers apply to XML body the same way.
How do you keep tests independent of environment URLs?
Set base URI via RestAssured.baseURI (global) or a RequestSpec — read from env var/config file. Tests just use get("/users") without hardcoding the host.