Introduction

In our previous post, we explored File Upload and Download in REST Assured, learning how to handle file operations in API testing. Now, we’ll dive into Error Handling, a crucial aspect of API testing that ensures your tests can gracefully handle and validate error responses from APIs. This guide is designed for beginners and experienced developers, providing clear explanations and practical examples to master error handling in REST Assured.

Key Point: Error handling in REST Assured involves validating error responses, such as 400 Bad Request or 404 Not Found, to ensure APIs behave correctly under invalid or unexpected conditions.

What is Error Handling in API Testing?

Error Handling involves testing how an API responds to invalid inputs, unauthorized requests, or unavailable resources. Common error scenarios include:

  • Client Errors: 400 (Bad Request), 401 (Unauthorized), 404 (Not Found).
  • Server Errors: 500 (Internal Server Error), 503 (Service Unavailable).

In REST Assured, you validate error responses by checking status codes, error messages, and response bodies. This ensures the API provides meaningful feedback and adheres to its specifications.

Error Handling in REST Assured

Let’s explore how to handle and validate error responses using REST Assured, with examples using the public API https://jsonplaceholder.typicode.com or illustrative APIs where needed.

Option 1: Validating 404 Not Found

Test an API’s response when a resource doesn’t exist.


import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

public class NotFoundErrorTest {

    @Test
    public void testNotFoundError() {
        RestAssured.baseURI = "https://jsonplaceholder.typicode.com";

        given()
            .pathParam("postId", 9999) // Non-existent ID
            .when()
                .get("/posts/{postId}")
            .then()
                .statusCode(404)
                .body(isEmptyOrNullString());
    }
}

Explanation:

  • pathParam("postId", 9999): Requests a non-existent post ID.
  • statusCode(404): Validates the API returns a 404 Not Found status.
  • body(isEmptyOrNullString()): Ensures the response body is empty, as expected for a 404 response in this API.
Important: Check the API documentation for expected error response formats, as some APIs may return a JSON body with an error message for 404 errors.

Option 2: Validating 400 Bad Request

Test an API’s response to invalid input, such as a malformed request body.


import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

public class BadRequestErrorTest {

    @Test
    public void testBadRequestError() {
        RestAssured.baseURI = "https://jsonplaceholder.typicode.com";

        String invalidJson = "{\"title\": \"\", \"body\": \"Test\", \"userId\": -1}"; // Invalid userId

        given()
            .contentType("application/json")
            .body(invalidJson)
            .when()
                .post("/posts")
            .then()
                .statusCode(400)
                .body("error", containsString("Invalid userId"));
    }
}

Explanation:

  • body(invalidJson): Sends a JSON payload with an invalid userId.
  • statusCode(400): Expects a 400 Bad Request status.
  • body("error", containsString("Invalid userId")): Validates the error message in the response body.
  • Note: jsonplaceholder.typicode.com may not return 400 for this case; replace with an API that enforces input validation for real-world testing.

Option 3: Validating 401 Unauthorized

Test an API’s response to an unauthorized request (e.g., missing or invalid authentication).


import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

public class UnauthorizedErrorTest {

    @Test
    public void testUnauthorizedError() {
        RestAssured.baseURI = "https://api.example.com"; // Replace with an auth-protected API

        given()
            .when()
                .get("/protected")
            .then()
                .statusCode(401)
                .body("message", containsString("Unauthorized"));
    }
}

Explanation:

  • No authentication is provided, triggering a 401 Unauthorized response.
  • statusCode(401): Validates the unauthorized status.
  • body("message", containsString("Unauthorized")): Checks the error message.
  • Note: Use an API requiring authentication (e.g., https://reqres.in with protected endpoints) for testing.

Option 4: Using Request Specification for Error Testing

Use a Request Specification to centralize settings for error handling tests.


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.*;

public class ErrorSpecTest {

    private static RequestSpecification requestSpec;

    @BeforeAll
    public static void setup() {
        requestSpec = new RequestSpecBuilder()
            .setBaseUri("https://jsonplaceholder.typicode.com")
            .setContentType("application/json")
            .build();
    }

    @Test
    public void testNotFoundWithSpec() {
        given()
            .spec(requestSpec)
            .pathParam("postId", 9999)
            .when()
                .get("/posts/{postId}")
            .then()
                .statusCode(404)
                .body(isEmptyOrNullString());
    }
}

Explanation:

  • requestSpec: Defines the base URI and content type.
  • spec(requestSpec): Applies the specification to the test.
  • The test validates a 404 error for a non-existent resource.
Pro Tip: Use Request Specifications to streamline error handling tests, ensuring consistent settings across multiple error scenarios.

Step 1: Extracting and Validating Error Responses

Extract the response to perform detailed validation of error messages or fields.


import io.restassured.RestAssured;
import io.restassured.response.Response;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.junit.jupiter.api.Assertions.*;

public class ExtractErrorTest {

    @Test
    public void testExtractErrorResponse() {
        RestAssured.baseURI = "https://api.example.com"; // Replace with an API that returns error details

        Response response = given()
            .contentType("application/json")
            .body("{\"invalid\": \"data\"}")
            .when()
                .post("/invalid-endpoint")
            .then()
                .statusCode(400)
                .extract().response();

        String errorMessage = response.path("error.message");
        String errorCode = response.path("error.code");

        assertEquals("Invalid request data", errorMessage);
        assertEquals("BAD_REQUEST", errorCode);

        System.out.println("Error: " + errorMessage + ", Code: " + errorCode);
    }
}

Explanation:

  • extract().response(): Extracts the full response for detailed validation.
  • response.path("error.message"): Retrieves specific error fields from the JSON response.
  • assertEquals: Validates the error details programmatically.

Step 2: Handling Server Errors (500)

Test an API’s response to a server error scenario.


import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

public class ServerErrorTest {

    @Test
    public void testServerError() {
        RestAssured.baseURI = "https://api.example.com"; // Replace with an API that can trigger 500

        given()
            .when()
                .get("/trigger-error")
            .then()
                .statusCode(500)
                .body("error", containsString("Internal Server Error"));
    }
}

Explanation:

  • statusCode(500): Expects a 500 Internal Server Error.
  • body("error", containsString("Internal Server Error")): Validates the error message.
  • Note: Use an API or endpoint designed to simulate server errors for testing.

Step 3: Verify Setup with pom.xml

Ensure your pom.xml includes dependencies for REST Assured and JUnit:



    
        io.rest-assured
        rest-assured
        5.4.0
        test
    
    
        org.junit.jupiter
        junit-jupiter
        5.10.2
        test
    
    
        org.hamcrest
        hamcrest
        2.2
        test
    

Run the tests using mvn test or your IDE’s test runner to confirm the setup.

Tips for Beginners

  • Check API Documentation: Understand the error codes and response formats the API returns for different scenarios.
  • Validate Error Messages: Ensure error responses include meaningful messages for debugging.
  • Test Edge Cases: Include invalid inputs, missing fields, or unauthorized requests in your tests.
  • Enable Logging: Use RestAssured.enableLoggingOfRequestAndResponseIfValidationFails() to debug error handling issues.
Troubleshooting Tip: If error validations fail, use logging to inspect the request and response. Verify the status code, headers, and body match the API’s error response structure.

What’s Next?

In the next post, we’ll cover Mocking APIs, exploring how to simulate API behavior using REST Assured for testing in isolated environments. Stay tuned for more hands-on examples!