Introduction
In our REST Assured series, we’ve covered topics like Serialization and Deserialization, Error Handling, and advanced techniques such as Security Testing. Now, we’ll explore PATCH Requests, a powerful HTTP method for partially updating resources in RESTful APIs. This guide demonstrates how to send and validate PATCH requests using REST Assured, with practical examples and best practices. It’s designed for beginners and experienced developers, providing clear explanations to enhance your API testing skills.
Key Point: PATCH requests with REST Assured allow you to test partial updates to API resources efficiently, ensuring accurate modifications and proper server responses.
What is a PATCH Request?
A PATCH Request is an HTTP method used to apply partial updates to a resource, unlike PUT, which replaces the entire resource. For example, to update only the title of a blog post without modifying its content or author, you’d use PATCH.
Key characteristics of PATCH requests:
- Partial Updates: Modifies only specified fields of a resource.
- Idempotency: Some PATCH operations are idempotent (repeated requests yield the same result), depending on the API design.
- Payload: Typically includes a JSON or XML body with fields to update.
REST Assured simplifies PATCH request testing by providing intuitive methods to send requests, set payloads, and validate responses. We’ll use the public API https://jsonplaceholder.typicode.com
, which supports mock PATCH operations, for our examples.
Setting Up for PATCH Request Testing
To test PATCH requests, we’ll use REST Assured, JUnit, and Allure for reporting. Ensure your Maven project is configured with the necessary dependencies.
Here’s the pom.xml
with required dependencies:
11
11
2.27.0
1.9.22
io.rest-assured
rest-assured
5.4.0
test
org.junit.jupiter
junit-jupiter
5.10.2
test
org.hamcrest
hamcrest
2.2
test
com.fasterxml.jackson.core
jackson-databind
2.15.2
test
io.qameta.allure
allure-junit5
${allure.version}
test
org.apache.maven.plugins
maven-surefire-plugin
3.2.5
-javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
org.aspectj
aspectjweaver
${aspectj.version}
io.qameta.allure
allure-maven
2.12.0
${allure.version}
No additional setup is needed for https://jsonplaceholder.typicode.com
, as it supports mock PATCH requests.
Sending and Validating PATCH Requests
Let’s explore how to send PATCH requests using REST Assured, validate responses, and handle common scenarios.
Example 1: Basic PATCH Request
Send a PATCH request to update the title of a post and validate the response.
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
@Feature("PATCH Request Testing")
public class BasicPatchTest {
@Test
@Description("Verify partial update of a post title using PATCH")
public void testPatchPostTitle() {
RestAssured.baseURI = "https://jsonplaceholder.typicode.com";
String requestBody = "{\"title\": \"Updated Title\"}";
given()
.contentType("application/json")
.body(requestBody)
.log().all()
.when()
.patch("/posts/1")
.then()
.statusCode(200)
.body("id", equalTo(1))
.body("title", equalTo("Updated Title"))
.body("body", notNullValue()); // Ensure other fields remain unchanged
}
}
Explanation:
patch("/posts/1")
: Sends a PATCH request to update post ID 1.requestBody
: Specifies only thetitle
field to update.- Validates the status code, updated title, and ensures other fields (e.g.,
body
) are preserved.
Important: Unlike PUT, PATCH updates only the fields provided in the request body, leaving others unchanged.
Example 2: PATCH with Serialization
Use a Java object to serialize the request body for cleaner code and type safety.
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import com.fasterxml.jackson.annotation.JsonInclude;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
@JsonInclude(JsonInclude.Include.NON_NULL)
class PostUpdate {
private String title;
private Integer userId;
// Getters and setters
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public Integer getUserId() { return userId; }
public void setUserId(Integer userId) { this.userId = userId; }
}
@Feature("PATCH Request Testing")
public class PatchSerializationTest {
@Test
@Description("Verify PATCH request with serialized Java object")
public void testPatchWithSerialization() {
RestAssured.baseURI = "https://jsonplaceholder.typicode.com";
PostUpdate update = new PostUpdate();
update.setTitle("New Title");
update.setUserId(1);
given()
.contentType("application/json")
.body(update)
.log().all()
.when()
.patch("/posts/1")
.then()
.statusCode(200)
.body("title", equalTo("New Title"))
.body("userId", equalTo(1))
.body("id", equalTo(1));
}
}
Explanation:
PostUpdate
: A Java class with fields to update, annotated with@JsonInclude
to exclude null fields.body(update)
: Serializes the object to JSON automatically using Jackson.- Validates the updated fields in the response.
Pro Tip: Use serialization to reduce hard-coded JSON strings, improving maintainability and reducing errors.
Example 3: Handling PATCH Error Scenarios
Test error cases, such as invalid payloads or non-existent resources, to ensure robust API behavior.
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
@Feature("PATCH Request Testing")
public class PatchErrorTest {
@Test
@Description("Verify error response for invalid PATCH payload")
public void testInvalidPayload() {
RestAssured.baseURI = "https://jsonplaceholder.typicode.com";
String invalidBody = "{\"title\": \"\"}"; // Empty title
given()
.contentType("application/json")
.body(invalidBody)
.log().ifValidationFails()
.when()
.patch("/posts/1")
.then()
.statusCode(400); // Expect bad request (mocked behavior)
}
@Test
@Description("Verify error response for non-existent resource")
public void testNonExistentResource() {
RestAssured.baseURI = "https://jsonplaceholder.typicode.com";
String requestBody = "{\"title\": \"Updated Title\"}";
given()
.contentType("application/json")
.body(requestBody)
.log().ifValidationFails()
.when()
.patch("/posts/9999")
.then()
.statusCode(404); // Expect not found
}
}
Explanation:
- Invalid Payload: Tests a PATCH with an empty title, expecting a 400 Bad Request (note:
jsonplaceholder.typicode.com
may return 200; adjust for real APIs). - Non-Existent Resource: Tests a PATCH for a non-existent post ID, expecting a 404 Not Found.
log().ifValidationFails()
: Logs details only on failure to reduce console clutter.
Using Request Specification for PATCH Requests
Centralize PATCH request settings with a RequestSpecification for consistency.
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.specification.RequestSpecification;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
@Feature("PATCH Request Testing")
public class PatchSpecTest {
private static RequestSpecification requestSpec;
@BeforeAll
public static void setup() {
requestSpec = new RequestSpecBuilder()
.setBaseUri("https://jsonplaceholder.typicode.com")
.setContentType("application/json")
.addHeader("Accept", "application/json")
.log(LogDetail.ALL)
.build();
}
@Test
@Description("Verify PATCH request using request specification")
public void testPatchWithSpec() {
String requestBody = "{\"title\": \"Spec Updated Title\"}";
given()
.spec(requestSpec)
.body(requestBody)
.when()
.patch("/posts/1")
.then()
.statusCode(200)
.body("title", equalTo("Spec Updated Title"));
}
}
Explanation:
requestSpec
: Defines the base URI, content type, headers, and logging.spec(requestSpec)
: Applies the specification to the PATCH request.
Integrating with Allure Reporting
Use Allure to document PATCH test results, attaching request/response details for clarity.
import io.qameta.allure.Allure;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
@Feature("PATCH Request Testing")
public class AllurePatchTest {
@Test
@Description("Verify PATCH request with Allure reporting")
public void testPatchWithAllure() {
RestAssured.baseURI = "https://jsonplaceholder.typicode.com";
String requestBody = "{\"title\": \"Allure Updated Title\"}";
Allure.addAttachment("Request Body", "application/json", requestBody, ".json");
Response response = given()
.contentType("application/json")
.body(requestBody)
.log().all()
.when()
.patch("/posts/1")
.then()
.statusCode(200)
.body("title", equalTo("Allure Updated Title"))
.extract().response();
Allure.addAttachment("Response Body", "application/json", response.asString(), ".json");
}
}
Explanation:
Allure.addAttachment
: Attaches the request and response bodies to the Allure report.- Run
mvn clean test
andmvn allure:serve
to view the report.
Integrating with CI/CD
Add PATCH tests to a GitHub Actions pipeline for automated execution.
Create or update .github/workflows/ci.yml
:
name: REST Assured CI Pipeline
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 11
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'temurin'
- name: Cache Maven dependencies
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-maven-
- name: Run tests
run: mvn clean test
- name: Generate Allure report
run: mvn allure:report
- name: Upload Allure results
if: always()
uses: actions/upload-artifact@v4
with:
name: allure-results
path: target/allure-results
- name: Publish Allure report
if: always()
uses: simple-elf/allure-report-action@v1.7
with:
allure_results: target/allure-results
gh_pages: gh-pages
allure_report: allure-report
- name: Deploy report to GitHub Pages
if: always()
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: allure-report
Explanation:
- Runs PATCH tests with
mvn clean test
. - Publishes Allure reports to GitHub Pages for visibility.
Tips for Beginners
- Test Minimal Updates: Send only the fields you want to update in the PATCH request to verify partial modification.
- Validate Unchanged Fields: Ensure non-updated fields remain intact in the response.
- Use Serialization: Leverage Java objects for complex payloads to improve code clarity.
- Enable Logging: Use
log().ifValidationFails()
to debug failures efficiently.
Troubleshooting Tip: If a PATCH request fails, check the request body format, content type, and API documentation for supported fields. Enable log().all()
to inspect request/response details.
What’s Next?
This post enhances our REST Assured series by covering PATCH requests, a key HTTP method for API updates. To continue your learning, consider exploring:
- GraphQL Testing: Testing GraphQL APIs with REST Assured.
- Advanced Allure Reporting: Customizing reports with additional metadata.
- End-to-End Testing: Combining REST Assured with Selenium for UI and API testing.