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 the title 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 and mvn 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.
Stay tuned for more testing tutorials!