REST Assured Tutorial: Getting Started with Java API Testing
Testing REST APIs in Java has a reputation for being verbose. You set up an HTTP client, construct requests, execute them, parse the response, and then write assertions against the parsed data — often 20+ lines for a simple GET test. REST Assured changes this. It provides a fluent, BDD-style API that makes API tests readable and concise.
This tutorial walks through setting up REST Assured and writing your first API tests from scratch.
What Is REST Assured?
REST Assured is a Java library for testing and validating REST services. It sits on top of Apache HttpClient and provides a domain-specific language (DSL) for writing HTTP tests that read almost like English:
given()
.baseUri("https://api.example.com")
.header("Authorization", "Bearer " + token)
.when()
.get("/users/42")
.then()
.statusCode(200)
.body("name", equalTo("Alice"))
.body("email", containsString("@example.com"));That's a complete test. No response parsing, no manual assertion construction. REST Assured handles the HTTP layer and provides Hamcrest matchers for assertions.
Why Choose REST Assured?
REST Assured is the dominant HTTP testing library in the Java ecosystem for good reasons:
- Mature and stable — over a decade of production use
- BDD syntax — Given/When/Then maps naturally to test scenarios
- Rich matchers — Hamcrest integration for flexible assertions
- JSON/XML support — built-in path extraction for both formats
- Schema validation — validate responses against JSON Schema
- Spring Boot integration — first-class support for testing Spring applications
- TestNG and JUnit support — works with both major Java test runners
- Authentication helpers — OAuth 2.0, JWT, Basic Auth, and more built-in
Project Setup
Maven
Add REST Assured to your pom.xml:
<dependencies>
<!-- REST Assured core -->
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>5.4.0</version>
<scope>test</scope>
</dependency>
<!-- JSON path support -->
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>json-path</artifactId>
<version>5.4.0</version>
<scope>test</scope>
</dependency>
<!-- XML path support (optional) -->
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>xml-path</artifactId>
<version>5.4.0</version>
<scope>test</scope>
</dependency>
<!-- JUnit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<!-- Hamcrest (for matchers) -->
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
</dependencies>Gradle
dependencies {
testImplementation 'io.rest-assured:rest-assured:5.4.0'
testImplementation 'io.rest-assured:json-path:5.4.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
testImplementation 'org.hamcrest:hamcrest:2.2'
}
test {
useJUnitPlatform()
}Your First Test
Create a test class in src/test/java:
import io.restassured.RestAssured;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
public class ApiTest {
@BeforeAll
static void setup() {
RestAssured.baseURI = "https://jsonplaceholder.typicode.com";
}
@Test
void shouldGetUser() {
given()
.when()
.get("/users/1")
.then()
.statusCode(200)
.body("id", equalTo(1))
.body("name", equalTo("Leanne Graham"))
.body("email", containsString("@"));
}
@Test
void shouldGetAllPosts() {
given()
.when()
.get("/posts")
.then()
.statusCode(200)
.body("size()", equalTo(100))
.body("[0].userId", notNullValue())
.body("[0].title", not(emptyString()));
}
@Test
void shouldCreatePost() {
String requestBody = """
{
"title": "REST Assured Tutorial",
"body": "Testing APIs with Java",
"userId": 1
}
""";
given()
.contentType("application/json")
.body(requestBody)
.when()
.post("/posts")
.then()
.statusCode(201)
.body("title", equalTo("REST Assured Tutorial"))
.body("id", notNullValue());
}
}Run with Maven:
mvn test -Dtest=ApiTestUnderstanding the BDD Syntax
REST Assured's fluent API follows BDD structure:
given() // Setup: headers, params, body, auth
.header("Authorization", "Bearer token")
.queryParam("page", 1)
.body(requestBody)
.when() // Action: HTTP method + path
.get("/endpoint") // GET
.post("/endpoint") // POST
.put("/endpoint") // PUT
.patch("/endpoint") // PATCH
.delete("/endpoint") // DELETE
.then() // Assertions
.statusCode(200)
.body("field", equalTo("value"))
.header("Content-Type", containsString("application/json"));The given() section is optional when you have no setup:
// Minimal test
when()
.get("/health")
.then()
.statusCode(200);Request Configuration
Headers
given()
.header("Authorization", "Bearer " + token)
.header("Accept", "application/json")
.header("X-Request-ID", UUID.randomUUID().toString())
.when()
.get("/protected-resource")
.then()
.statusCode(200);Query Parameters
given()
.queryParam("page", 1)
.queryParam("limit", 20)
.queryParam("sort", "name")
.queryParam("order", "asc")
.when()
.get("/users")
.then()
.statusCode(200);Path Parameters
int userId = 42;
given()
.pathParam("id", userId)
.when()
.get("/users/{id}")
.then()
.statusCode(200)
.body("id", equalTo(userId));Request Body
// JSON string
given()
.contentType("application/json")
.body("{\"name\": \"Alice\", \"email\": \"alice@example.com\"}")
.when()
.post("/users");
// Map (auto-serialized to JSON)
Map<String, Object> user = new HashMap<>();
user.put("name", "Alice");
user.put("email", "alice@example.com");
given()
.contentType(ContentType.JSON)
.body(user)
.when()
.post("/users");
// Object (with Jackson)
User user = new User("Alice", "alice@example.com");
given()
.contentType(ContentType.JSON)
.body(user)
.when()
.post("/users");Response Assertions
Status Code and Headers
.then()
.statusCode(200)
.statusLine("HTTP/1.1 200 OK")
.header("Content-Type", "application/json; charset=utf-8")
.header("X-Rate-Limit", notNullValue())
.headers("Content-Type", containsString("json"),
"Cache-Control", notNullValue());Body Assertions with JsonPath
.then()
.body("name", equalTo("Alice")) // String equality
.body("age", greaterThan(18)) // Number comparison
.body("active", is(true)) // Boolean
.body("tags", hasItems("java", "testing")) // Array contains
.body("tags.size()", equalTo(3)) // Array length
.body("address.city", equalTo("New York")) // Nested object
.body("orders[0].total", greaterThan(0.0f)) // Array index
.body("orders.id", hasItems(101, 102, 103)); // All array field valuesExtracting Response Values
// Extract a single value
String name = given()
.when()
.get("/users/1")
.then()
.statusCode(200)
.extract()
.path("name");
System.out.println("User name: " + name);
// Extract the full response body
String responseBody = given()
.when()
.get("/users/1")
.then()
.extract()
.body()
.asString();
// Extract as a typed object (with Jackson)
User user = given()
.when()
.get("/users/1")
.then()
.extract()
.as(User.class);
// Extract header
String location = given()
.contentType(ContentType.JSON)
.body(newUserJson)
.when()
.post("/users")
.then()
.statusCode(201)
.extract()
.header("Location");Global Configuration
Avoid repeating base URI, headers, and auth in every test:
import io.restassured.RestAssured;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.specification.RequestSpecification;
import org.junit.jupiter.api.BeforeAll;
public class BaseApiTest {
protected static RequestSpecification requestSpec;
@BeforeAll
static void setupRestAssured() {
// Global base URI
RestAssured.baseURI = System.getProperty("api.base.url", "https://api.example.com");
// Enable request/response logging for failed tests
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
// Build a reusable request specification
requestSpec = new RequestSpecBuilder()
.setContentType(ContentType.JSON)
.setAccept(ContentType.JSON)
.addHeader("Authorization", "Bearer " + getAuthToken())
.addHeader("X-API-Version", "v2")
.build();
}
private static String getAuthToken() {
// Get token from env var or test config
return System.getenv().getOrDefault("API_TOKEN", "test-token-123");
}
}Use in test classes:
public class UsersApiTest extends BaseApiTest {
@Test
void shouldGetCurrentUser() {
given()
.spec(requestSpec)
.when()
.get("/me")
.then()
.statusCode(200)
.body("email", not(emptyString()));
}
}Common Matchers Reference
REST Assured uses Hamcrest matchers. Common ones:
import static org.hamcrest.Matchers.*;
// Equality
equalTo("value")
not(equalTo("other"))
is("value")
// Strings
containsString("substring")
startsWith("prefix")
endsWith("suffix")
matchesPattern("\\d{3}-\\d{4}")
emptyString()
not(emptyOrNullString())
// Numbers
greaterThan(5)
greaterThanOrEqualTo(5)
lessThan(100)
closeTo(9.99, 0.01) // Within delta
// Collections
hasItem("value")
hasItems("a", "b", "c")
contains("a", "b", "c") // Exact order
containsInAnyOrder("c", "a", "b")
hasSize(5)
empty()
not(empty())
// Nulls
nullValue()
notNullValue()
// Type
instanceOf(Integer.class)Running Tests
Maven
# Run all tests
mvn <span class="hljs-built_in">test
<span class="hljs-comment"># Run specific class
mvn <span class="hljs-built_in">test -Dtest=UsersApiTest
<span class="hljs-comment"># Run specific method
mvn <span class="hljs-built_in">test -Dtest=UsersApiTest#shouldGetCurrentUser
<span class="hljs-comment"># With system properties
mvn <span class="hljs-built_in">test -Dapi.base.url=https://staging.api.example.comJUnit 5 in IDE
Tests run directly from your IDE with the play button. JUnit 5 provides clear pass/fail output per test method.
Logging for Debugging
REST Assured can log requests and responses:
// Log everything
given()
.log().all()
.when()
.get("/users/1")
.then()
.log().all()
.statusCode(200);
// Log only on failure
given()
.log().ifValidationFails()
.when()
.get("/users/1")
.then()
.log().ifValidationFails()
.statusCode(200);
// Log specific parts
given()
.log().headers()
.log().body()
.when()
.post("/users")
.then()
.log().status()
.log().body();Enable globally for all failing tests (recommended for CI):
@BeforeAll
static void setup() {
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
}Next Steps
With REST Assured set up, you're ready to explore:
- Spring Boot integration — testing your own Spring REST endpoints with
RestAssuredMockMvc - Authentication — OAuth2, JWT, and Basic Auth patterns
- TestNG integration — using REST Assured with TestNG for parameterized tests
- BDD style — organizing tests with Given-When-Then using REST Assured's DSL more formally
- Schema validation — validating response structure against JSON Schema
REST Assured is the foundation for Java API testing. Its fluent API scales from simple smoke tests to comprehensive API test suites that cover authentication, pagination, error handling, and complex business workflows.