REST Assured API Testing
API testing skill using REST Assured for Java, covering request specifications, response validation, authentication, JSON schema validation, and serialization.
Best use case
REST Assured API Testing is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
API testing skill using REST Assured for Java, covering request specifications, response validation, authentication, JSON schema validation, and serialization.
Teams using REST Assured API Testing should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/rest-assured-api/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How REST Assured API Testing Compares
| Feature / Agent | REST Assured API Testing | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
API testing skill using REST Assured for Java, covering request specifications, response validation, authentication, JSON schema validation, and serialization.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# REST Assured API Testing Skill
You are an expert QA automation engineer specializing in REST Assured API testing with Java. When the user asks you to write, review, or debug REST Assured tests, follow these detailed instructions.
## Core Principles
1. **Given-When-Then** -- Structure every test using REST Assured's BDD syntax.
2. **Request/Response specs** -- Reuse common configurations via specification builders.
3. **Type-safe models** -- Deserialize responses into POJOs for compile-time safety.
4. **Schema validation** -- Validate response structure with JSON Schema.
5. **Logging** -- Log requests and responses on failure for debugging.
## Project Structure
```
src/
main/java/com/example/
models/
User.java
Product.java
ApiError.java
specs/
RequestSpecs.java
ResponseSpecs.java
utils/
AuthHelper.java
TestDataHelper.java
test/java/com/example/
tests/
BaseApiTest.java
UsersApiTest.java
ProductsApiTest.java
AuthApiTest.java
schemas/
user-schema.json
product-schema.json
error-schema.json
test/resources/
test-data/
users.json
config.properties
pom.xml
```
## Maven Dependencies
```xml
<dependencies>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>5.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>json-schema-validator</artifactId>
<version>5.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>json-path</artifactId>
<version>5.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
</dependencies>
```
## POJO Models
```java
package com.example.models;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class User {
private String id;
private String email;
private String firstName;
private String lastName;
private String role;
private boolean active;
private String createdAt;
private String updatedAt;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CreateUserRequest {
private String email;
private String firstName;
private String lastName;
private String password;
private String role;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class ApiError {
private int statusCode;
private String message;
private String error;
private Map<String, List<String>> details;
}
```
## Request and Response Specifications
```java
package com.example.specs;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.builder.ResponseSpecBuilder;
import io.restassured.filter.log.LogDetail;
import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification;
import static org.hamcrest.Matchers.*;
public class RequestSpecs {
public static RequestSpecification baseSpec() {
return new RequestSpecBuilder()
.setBaseUri(System.getProperty("base.url", "http://localhost:3000"))
.setBasePath("/api")
.setContentType(ContentType.JSON)
.setAccept(ContentType.JSON)
.log(LogDetail.ALL)
.build();
}
public static RequestSpecification authSpec(String token) {
return new RequestSpecBuilder()
.addRequestSpecification(baseSpec())
.addHeader("Authorization", "Bearer " + token)
.build();
}
public static RequestSpecification adminSpec() {
String token = AuthHelper.getAdminToken();
return authSpec(token);
}
}
public class ResponseSpecs {
public static ResponseSpecification successResponse() {
return new ResponseSpecBuilder()
.expectStatusCode(200)
.expectContentType(ContentType.JSON)
.expectResponseTime(lessThan(5000L))
.log(LogDetail.ALL)
.build();
}
public static ResponseSpecification createdResponse() {
return new ResponseSpecBuilder()
.expectStatusCode(201)
.expectContentType(ContentType.JSON)
.build();
}
public static ResponseSpecification noContentResponse() {
return new ResponseSpecBuilder()
.expectStatusCode(204)
.build();
}
public static ResponseSpecification notFoundResponse() {
return new ResponseSpecBuilder()
.expectStatusCode(404)
.expectContentType(ContentType.JSON)
.expectBody("message", notNullValue())
.build();
}
public static ResponseSpecification validationErrorResponse() {
return new ResponseSpecBuilder()
.expectStatusCode(400)
.expectContentType(ContentType.JSON)
.expectBody("message", notNullValue())
.build();
}
}
```
## Base Test Class
```java
package com.example.tests;
import com.example.specs.RequestSpecs;
import io.restassured.RestAssured;
import io.restassured.specification.RequestSpecification;
import org.testng.annotations.BeforeClass;
public abstract class BaseApiTest {
protected RequestSpecification requestSpec;
@BeforeClass
public void setUp() {
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
requestSpec = RequestSpecs.baseSpec();
}
protected String getAuthToken() {
return given()
.spec(requestSpec)
.body(Map.of(
"email", "admin@example.com",
"password", "AdminPass123!"
))
.when()
.post("/auth/login")
.then()
.statusCode(200)
.extract()
.path("token");
}
}
```
## Writing Tests
### CRUD Tests
```java
package com.example.tests;
import com.example.models.User;
import com.example.models.CreateUserRequest;
import com.example.specs.ResponseSpecs;
import io.restassured.response.Response;
import org.testng.annotations.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.*;
public class UsersApiTest extends BaseApiTest {
@Test(description = "Create a new user")
public void testCreateUser() {
CreateUserRequest newUser = CreateUserRequest.builder()
.email("test-" + System.currentTimeMillis() + "@example.com")
.firstName("Test")
.lastName("User")
.password("SecurePass123!")
.role("user")
.build();
User createdUser = given()
.spec(requestSpec)
.body(newUser)
.when()
.post("/users")
.then()
.spec(ResponseSpecs.createdResponse())
.body("id", notNullValue())
.body("email", equalTo(newUser.getEmail()))
.body("firstName", equalTo(newUser.getFirstName()))
.body("role", equalTo("user"))
.extract()
.as(User.class);
assertThat(createdUser.getId()).isNotEmpty();
assertThat(createdUser.getEmail()).isEqualTo(newUser.getEmail());
}
@Test(description = "Get user by ID")
public void testGetUserById() {
// First create a user
String userId = createTestUser();
given()
.spec(requestSpec)
.pathParam("id", userId)
.when()
.get("/users/{id}")
.then()
.spec(ResponseSpecs.successResponse())
.body("id", equalTo(userId))
.body("email", notNullValue())
.body("firstName", notNullValue());
}
@Test(description = "Update user")
public void testUpdateUser() {
String userId = createTestUser();
given()
.spec(requestSpec)
.pathParam("id", userId)
.body(Map.of("firstName", "Updated"))
.when()
.patch("/users/{id}")
.then()
.spec(ResponseSpecs.successResponse())
.body("firstName", equalTo("Updated"));
}
@Test(description = "Delete user")
public void testDeleteUser() {
String userId = createTestUser();
given()
.spec(requestSpec)
.pathParam("id", userId)
.when()
.delete("/users/{id}")
.then()
.spec(ResponseSpecs.noContentResponse());
// Verify deletion
given()
.spec(requestSpec)
.pathParam("id", userId)
.when()
.get("/users/{id}")
.then()
.spec(ResponseSpecs.notFoundResponse());
}
@Test(description = "List users with pagination")
public void testListUsersWithPagination() {
given()
.spec(requestSpec)
.queryParam("page", 1)
.queryParam("pageSize", 5)
.when()
.get("/users")
.then()
.spec(ResponseSpecs.successResponse())
.body("data", hasSize(lessThanOrEqualTo(5)))
.body("page", equalTo(1))
.body("pageSize", equalTo(5))
.body("total", greaterThanOrEqualTo(0));
}
private String createTestUser() {
CreateUserRequest user = CreateUserRequest.builder()
.email("test-" + System.currentTimeMillis() + "@example.com")
.firstName("Test")
.lastName("User")
.password("SecurePass123!")
.build();
return given()
.spec(requestSpec)
.body(user)
.post("/users")
.then()
.statusCode(201)
.extract()
.path("id");
}
}
```
### JSON Schema Validation
```json
// schemas/user-schema.json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["id", "email", "firstName", "lastName", "role", "createdAt"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"email": { "type": "string", "format": "email" },
"firstName": { "type": "string", "minLength": 1 },
"lastName": { "type": "string", "minLength": 1 },
"role": { "type": "string", "enum": ["admin", "user", "viewer"] },
"active": { "type": "boolean" },
"createdAt": { "type": "string", "format": "date-time" }
},
"additionalProperties": false
}
```
```java
import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath;
@Test(description = "Response should match JSON schema")
public void testUserResponseSchema() {
given()
.spec(requestSpec)
.when()
.get("/users/" + knownUserId)
.then()
.statusCode(200)
.body(matchesJsonSchemaInClasspath("schemas/user-schema.json"));
}
```
### Authentication Tests
```java
@Test(description = "Should authenticate with valid credentials")
public void testValidLogin() {
given()
.spec(requestSpec)
.body(Map.of(
"email", "admin@example.com",
"password", "AdminPass123!"
))
.when()
.post("/auth/login")
.then()
.statusCode(200)
.body("token", notNullValue())
.body("expiresIn", greaterThan(0))
.body("user.email", equalTo("admin@example.com"))
.body("user.role", equalTo("admin"));
}
@Test(description = "Should reject invalid credentials")
public void testInvalidLogin() {
given()
.spec(requestSpec)
.body(Map.of(
"email", "admin@example.com",
"password", "wrongpassword"
))
.when()
.post("/auth/login")
.then()
.statusCode(401)
.body("message", equalTo("Invalid credentials"));
}
@Test(description = "Protected endpoint requires auth token")
public void testProtectedEndpoint() {
// Without token
given()
.spec(requestSpec)
.when()
.get("/users/me")
.then()
.statusCode(401);
// With valid token
String token = getAuthToken();
given()
.spec(requestSpec)
.header("Authorization", "Bearer " + token)
.when()
.get("/users/me")
.then()
.statusCode(200)
.body("email", notNullValue());
}
```
### Validation Error Tests
```java
@Test(description = "Should return validation errors for invalid input")
public void testCreateUserValidation() {
// Missing required fields
given()
.spec(requestSpec)
.body(Map.of("firstName", "Test"))
.when()
.post("/users")
.then()
.statusCode(400)
.body("details.email", notNullValue())
.body("details.password", notNullValue());
}
@Test(description = "Should reject invalid email format")
public void testInvalidEmailFormat() {
given()
.spec(requestSpec)
.body(Map.of(
"email", "not-an-email",
"firstName", "Test",
"lastName", "User",
"password", "SecurePass123!"
))
.when()
.post("/users")
.then()
.statusCode(400)
.body("details.email[0]", containsString("valid email"));
}
```
### Response Extraction
```java
// Extract single value
String userId = given()
.spec(requestSpec)
.get("/users/me")
.then()
.extract()
.path("id");
// Extract as POJO
User user = given()
.spec(requestSpec)
.get("/users/me")
.then()
.extract()
.as(User.class);
// Extract list
List<User> users = given()
.spec(requestSpec)
.get("/users")
.then()
.extract()
.jsonPath()
.getList("data", User.class);
// Extract headers
String requestId = given()
.spec(requestSpec)
.get("/users")
.then()
.extract()
.header("X-Request-Id");
// Extract response time
long responseTime = given()
.spec(requestSpec)
.get("/health")
.then()
.extract()
.time();
```
### File Upload
```java
@Test(description = "Should upload a file")
public void testFileUpload() {
File file = new File("src/test/resources/test-data/sample.pdf");
given()
.spec(requestSpec)
.contentType(ContentType.MULTIPART)
.multiPart("file", file, "application/pdf")
.formParam("description", "Test upload")
.when()
.post("/files/upload")
.then()
.statusCode(201)
.body("filename", equalTo("sample.pdf"))
.body("size", greaterThan(0));
}
```
## Best Practices
1. **Use Given-When-Then structure** -- Every test should clearly separate setup, action, and assertion.
2. **Create reusable specs** -- Use `RequestSpecBuilder` and `ResponseSpecBuilder` for DRY code.
3. **Deserialize to POJOs** -- Type-safe models catch issues at compile time.
4. **Validate schemas** -- JSON schema validation ensures API contracts are honored.
5. **Log on failure** -- Use `enableLoggingOfRequestAndResponseIfValidationFails()`.
6. **Use Hamcrest matchers** -- They compose well with REST Assured assertions.
7. **Clean up test data** -- Delete created resources in `@AfterMethod`.
8. **Parameterize with DataProvider** -- Avoid duplicating tests for similar scenarios.
9. **Check response times** -- Include performance assertions in API tests.
10. **Test negative cases** -- Invalid input, missing auth, wrong methods.
## Anti-Patterns to Avoid
1. **Hardcoded base URLs** -- Use system properties or config files.
2. **No assertions** -- A test that only checks status code is incomplete.
3. **Chained test dependencies** -- Each test must stand alone.
4. **Verbose inline specs** -- Extract common settings into specs.
5. **Ignoring response headers** -- Headers contain important metadata.
6. **Not testing error responses** -- Error payloads should be validated too.
7. **Using `body()` with raw strings** -- Use POJOs or Map.of() for request bodies.
8. **Not validating response schema** -- Schema changes can break consumers.
9. **Testing only happy paths** -- Error handling is where most bugs hide.
10. **Synchronous test execution** -- Use TestNG parallel execution for faster feedback.Related Skills
Zod Schema Testing
Comprehensive testing patterns for Zod schemas covering validation testing, transform testing, error message verification, and integration with API endpoints and forms
YARA Rule Testing
Writing and testing YARA rules for malware detection, threat hunting, and file classification with rule validation and false-positive rate testing.
xUnit.net Testing
Comprehensive xUnit.net testing skill for writing reliable unit, integration, and acceptance tests in C# with [Fact], [Theory], fixtures, dependency injection, and parallel execution strategies.
XSS Testing Patterns
Cross-site scripting vulnerability testing covering reflected, stored, and DOM-based XSS with sanitization validation and CSP bypass detection.
XCUITest iOS Testing
iOS UI testing with XCUITest framework covering element queries, gesture simulation, accessibility testing, and Xcode test plan configuration.
Advanced WebSocket Testing
WebSocket testing including connection lifecycle, reconnection logic, message ordering, backpressure handling, and binary frame testing.
Webhook Testing
Testing webhook implementations including delivery verification, retry logic, signature validation, idempotency, and failure handling patterns.
Core Web Vitals Testing
Testing and monitoring Core Web Vitals (LCP, FID, CLS, INP, TTFB) to ensure web performance meets Google search ranking thresholds.
WCAG Accessibility Testing
Automated WCAG 2.2 AA/AAA compliance testing with axe-core, Pa11y, and manual testing patterns for keyboard navigation, screen readers, and color contrast.
WebAssembly Testing
Testing WebAssembly modules including compilation verification, memory management, interop testing, and performance benchmarking of WASM components.
Vue Test Utils Testing
Vue.js component testing using Vue Test Utils with mount/shallow mount, event simulation, Vuex/Pinia store testing, and composition API testing.
Voice Assistant Testing
Testing voice-activated applications including speech recognition accuracy, intent detection, dialog flow testing, and multi-language support.