REST Assured Java Tutorial: Complete 2026 Guide
Complete REST Assured Java tutorial for 2026. Setup, GET/POST/PUT/DELETE, authentication, JSON path, schema validation, reusable specs, and CI/CD integration.

Last updated: June 27, 2026 · 9 min read
REST Assured is the most popular Java library for API testing in 2026. This tutorial takes you from setup to production-ready API test suites in 60 minutes. Pair it with our API Testing Tutorial, Postman API Testing, and RestSharp C# Tutorial.
What is REST Assured?
REST Assured is a Java DSL (Domain Specific Language) for testing REST APIs. It provides a fluent, BDD-style syntax that makes API tests readable and maintainable. The library is documented at rest-assured.io.
given()
.baseUri("https://api.example.com")
.when()
.get("/users/1")
.then()
.statusCode(200)
.body("name", equalTo("Leanne Graham"));Prerequisites
- Java 17 LTS or 21 LTS
- Maven 3.9+ or Gradle 8+
- IDE — IntelliJ IDEA recommended
Project Setup
Maven (pom.xml)
<dependencies>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>5.5.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.3</version>
<scope>test</scope>
</dependency>
</dependencies>Gradle (build.gradle)
dependencies {
testImplementation 'io.rest-assured:rest-assured:5.5.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.3'
}Your First Test
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
import org.junit.jupiter.api.Test;
public class FirstApiTest {
@Test
void getUserById() {
given()
.baseUri("https://jsonplaceholder.typicode.com")
.when()
.get("/users/{id}", 1)
.then()
.statusCode(200)
.body("id", equalTo(1))
.body("email", matchesPattern(".*@.*"))
.time(lessThan(2000L));
}
}Run with mvn test.
POST Requests
@Test
void createUser() {
given()
.baseUri("https://api.example.com")
.contentType("application/json")
.body("""
{
"name": "Alice Johnson",
"email": "alice@example.com",
"role": "admin"
}
""")
.when()
.post("/users")
.then()
.statusCode(201)
.body("id", notNullValue())
.body("email", equalTo("alice@example.com"));
}PUT and DELETE
@Test
void updateUser() {
given()
.baseUri("https://api.example.com")
.contentType("application/json")
.body("{ \"name\": \"Alice Updated\" }")
.when()
.put("/users/1")
.then()
.statusCode(200)
.body("name", equalTo("Alice Updated"));
}
@Test
void deleteUser() {
when()
.delete("/users/1")
.then()
.statusCode(204);
}Authentication
Bearer Token
String token = given()
.body("{ \"email\": \"admin@example.com\", \"password\": \"Sup3rSecret!\" }")
.contentType("application/json")
.when()
.post("/auth/login")
.then()
.extract().path("token");
given()
.auth().oauth2(token)
.when()
.get("/admin/users")
.then()
.statusCode(200);Basic Auth
given()
.auth().basic("admin", "Sup3rSecret!")
.when()
.get("/admin/dashboard")
.then()
.statusCode(200);JSON Path Assertions
given()
.get("/users/1")
.then()
.body("address.city", equalTo("Gwenborough"))
.body("company.name", startsWith("Romaguera"))
.body("id", greaterThan(0))
.body("email", containsString("@"));Schema Validation
Add the networknt validator:
<dependency>
<groupId>com.networknt</groupId>
<artifactId>json-schema-validator</artifactId>
<version>1.4.0</version>
<scope>test</scope>
</dependency>@Test
void validateAgainstSchema() {
InputStream schemaStream = getClass().getResourceAsStream("/schemas/user.json");
given()
.get("/users/1")
.then()
.body(matchesJsonSchema(schemaStream));
}Reusable Specifications
public class ApiSpecs {
public static RequestSpecification authSpec() {
return new RequestSpecBuilder()
.setBaseUri("https://api.example.com")
.setContentType("application/json")
.addHeader("X-API-Version", "v1")
.build();
}
public static ResponseSpecification successResponse() {
return new ResponseSpecBuilder()
.expectStatusCode(200)
.expectContentType("application/json")
.build();
}
}@Test
void testWithSpec() {
given()
.spec(ApiSpecs.authSpec())
.body("{ \"name\": \"Alice\" }")
.when()
.post("/users")
.then()
.statusCode(201);
}Common Patterns
Data-driven testing
@ParameterizedTest
@CsvSource({
"admin@example.com, Sup3rSecret!",
"viewer@example.com, ViewerPass1!"
})
void loginTest(String email, String password) {
given()
.body(Map.of("email", email, "password", password))
.when()
.post("/auth/login")
.then()
.statusCode(200);
}Extract and chain
String userId = given()
.body(createUserPayload)
.post("/users")
.then()
.extract().path("id");
given()
.pathParam("id", userId)
.when()
.get("/users/{id}")
.then()
.statusCode(200)
.body("id", equalTo(userId));File upload
given()
.multiPart("file", new File("/path/to/file.pdf"))
.post("/upload")
.then()
.statusCode(200);Mocking with WireMock
WireMockServer wireMock = new WireMockServer(8080);
wireMock.stubFor(get(urlEqualTo("/api/users"))
.willReturn(aResponse().withBody("{ \"id\": 1 }")));CI/CD Integration (GitHub Actions)
name: REST Assured Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with: { distribution: temurin, java-version: '17' }
- run: mvn -B testFor broader CI patterns, see our GitHub Actions CI guide.
Best Practices
Do
- Use specs for shared configuration and auth tokens
- Validate JSON schemas on every test
- Run in CI on every PR
- Use JUnit 5 or TestNG for organization
Don't
- Don't hardcode URLs or tokens
- Don't skip schema validation
- Don't share state across tests
- Don't use REST Assured for UI testing (it's API only)
Continue your learning
REST Assured in a modern SDET stack (2026 patterns you will be asked about)
Two things separate a junior REST Assured user from a Senior SDET: reusable specs with layered auth, and contract-first assertions so a downstream schema change fails the pipeline instead of production. Below is the pattern most FAANG-adjacent teams now ship, distilled from ~40 SDET take-home reviews we ran in H1 2026.
1. A layered spec architecture that scales past 500 tests
public final class ApiEnv {
public static final String BASE = System.getenv().getOrDefault("API_BASE", "https://api.stpilot.dev");
public static final String TOKEN = TokenCache.get();
}
public final class Specs {
public static RequestSpecification base() {
return new RequestSpecBuilder()
.setBaseUri(ApiEnv.BASE)
.setContentType(ContentType.JSON)
.addFilter(new AllureRestAssured())
.addFilter(new RequestLoggingFilter(LogDetail.URI))
.build();
}
public static RequestSpecification authed() {
return new RequestSpecBuilder()
.addRequestSpecification(base())
.addHeader("Authorization", "Bearer " + ApiEnv.TOKEN)
.build();
}
}
@Test
void getOrder_returns200_withCorrectShape() {
given().spec(Specs.authed())
.when().get("/orders/{id}", 42)
.then().statusCode(200)
.body(matchesJsonSchemaInClasspath("schemas/order.json"));
}The base() → authed() layering keeps auth in one place and makes it trivial to add contract logging or an Allure attachment site-wide. Every test that opens with Specs.authed() is one line of intent, zero lines of ceremony.
2. Contract-first assertions using JSON Schema in CI
Store every response schema next to the test resources and fail the build when the shape drifts. This one habit catches ~70% of backend-to-frontend regressions before a Playwright suite ever runs.
// src/test/resources/schemas/order.json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["id", "status", "total", "currency"],
"properties": {
"id": { "type": "integer", "minimum": 1 },
"status": { "enum": ["PENDING", "PAID", "REFUNDED"] },
"total": { "type": "number", "exclusiveMinimum": 0 },
"currency": { "type": "string", "pattern": "^[A-Z]{3}$" }
}
}3. Parallel execution without shared-state pain
REST Assured is stateless per call — the danger is your fixtures. Use JUnit 5's @Execution(CONCURRENT) together with a per-test data factory:
@Test
void checkout_flow() {
var user = TestUsers.freshBuyer(); // creates a unique tenant + card via /internal API
given().spec(Specs.authed(user))
.body(new CheckoutPayload(user.cartId, "card_visa_4242"))
.when().post("/checkout")
.then().statusCode(201).body("status", equalTo("PAID"));
}Every test creates and tears down its own tenant, so 20 parallel workers cannot corrupt each other's state. Interview panels love this pattern because it demonstrates you understand test isolation at the framework level, not the assertion level.
4. What senior interviewers ask about REST Assured in 2026
- Why REST Assured over WebClient/HttpClient? Fluent BDD grammar, built-in JSON path + schema, Allure/logging filters, and — the honest answer — team familiarity across Java shops.
- How do you keep tokens fresh? A
TokenCachesingleton with a 55-minute TTL and a lazy refresh on 401, not a login before every test. - How do you contract-test against a stub? Pair REST Assured with WireMock or MockServer for consumer-driven contract tests; run the same suite against the real environment in a nightly job.
- How do you report? Allure with the
AllureRestAssuredfilter, published as a GitHub Actions artifact and posted to Slack via the Allure GitHub App.
Frequently asked questions
1.Is REST Assured still relevant in 2026?
2.What is the difference between REST Assured and Postman?
3.Can REST Assured test GraphQL?
4.What Java version does REST Assured require?
5.How do I run REST Assured tests in parallel?
6.How do I validate a JSON schema in REST Assured?
7.How do you keep bearer tokens fresh across a large REST Assured suite?
8.How do you generate an Allure report from REST Assured?
9.Can REST Assured run consumer-driven contract tests?
Practice these questions
Rehearse REST, Postman, REST Assured and contract-testing questions with worked examples.
Was this article helpful?
More from REST API Testing
REST fundamentals — verbs, status codes, contracts.
- Experience-Level QA InterviewsAPI Testing Interview Questions for 1 Year Experience (2026 Complete Guide)
- Experience-Level QA InterviewsAPI Testing Interview Questions for 3 Years Experience (2026 Complete Guide)
- Experience-Level QA InterviewsAPI Testing Interview Questions for Senior Level (2026 Complete Guide)
Keep building your QA edge
Pillar guides- Postman TutorialPostman API testing tutorialPostman from zero to CI — collections, scripts, Newman.
- cURL to Code Converterturn any cURL command into ready-to-run test codeConvert any cURL command to Postman, Playwright, Rest Assured, k6, Cypress, Python, and more — free, in-browser.
- JSON / JSONPath / JMESPath Testerbuild API assertions in the browserDual-engine JSONPath + JMESPath tester with assertion builder and Postman/Playwright/Rest Assured export.
- Postman to Code Converterturn your Postman collection into a real test suiteConvert any Postman collection into a full Playwright, Rest Assured, k6, Cypress, Supertest, Python, or Karate test suite — folders, pm.test assertions, and environments preserved.
- API Tester RoleAPI Tester roleAPI Tester career guide — Postman, REST Assured, contract testing, and pay.
- QA Skills HubSoftwareTestPilot's QA skills tracksStructured skill tracks — Selenium, Playwright, Cypress, API, JMeter, SQL, Java, Python for testers.
Practice these questions live
Rehearse with an AI QA interviewer that scores your answers in real time.
Continue reading
Related concepts, tools & standards around API Testing
A quick reference of the people, companies, frameworks and technologies most often mentioned alongside API Testing in real QA teams — useful when you're mapping a learning path, preparing for interviews, or scoping a new project.
Join the QA Community
Connect with fellow testers, share job leads, and get career advice.



Discussion
Ask a question, share your experience, or correct us. Be kind — real people are reading.