REST Assured: Java API Testing Framework for RESTful Services
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
- What is the given/when/then syntax in REST Assured?
- How do you extract a value from a response for use in another request?
- How do you validate a JSON response against a schema?
- How does REST Assured integrate with Spring Boot tests?
Answers:
- given() sets up request (headers, auth, body, params). when() specifies the HTTP method and endpoint. then() contains assertions on the response (status, body, headers).
- Use
.extract().path("field.name")to extract a single value, or.extract().response()to get the full response object for multiple extractions. - Add
io.rest-assured:json-schema-validatordependency. Use.body(matchesJsonSchemaInClasspath("schema.json"))to validate against a JSON schema file in the classpath. - 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." >}}
{{< 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