REST Assured

Why REST Assured is a strong Java API automation option and what matters in test architecture.

Updated:

Contents

REST Assured is a Java library with an expressive DSL for HTTP testing. It describes requests and verifies response status, headers, and body. It typically runs alongside JUnit Jupiter or another runner responsible for cases, setup, and overall execution results.

Java teams can reuse familiar builds, data types, and infrastructure. However, a convenient given–when–then chain does not establish test quality. QA needs business expectations, real integration boundaries, and controlled system state.

Dependencies and selected version

This example targets a Maven project with JDK 17+, io.rest-assured:rest-assured:6.2.0 in test scope, and configured JUnit Jupiter execution. REST Assured documentation lists that version as of the article's preparation. Pin runner and build-plugin versions according to their requirements.

Do not assume every new release is compatible with an existing suite. Review release notes, transitive dependencies, and API changes before upgrading. This example uses the standard JDK jdk.httpserver module to create a local server without an external application.

A self-contained HTTP response check

Save the class as src/test/java/OrderApiTest.java and run mvn -Dtest=OrderApiTest test in the prepared Maven project. The server selects an available port and returns a synthetic order. No real services, databases, or credentials are used.

java
import com.sun.net.httpserver.HttpServer;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;

class OrderApiTest {
  @Test
  void readsDraftOrder() throws Exception {
    var server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
    server.createContext("/orders/42", exchange -> {
      byte[] body = "{\"id\":42,\"status\":\"draft\"}"
          .getBytes(StandardCharsets.UTF_8);
      exchange.getResponseHeaders().set("Content-Type", "application/json");
      exchange.sendResponseHeaders(200, body.length);
      try (var output = exchange.getResponseBody()) {
        output.write(body);
      }
    });
    server.start();
    try {
      given().baseUri("http://127.0.0.1")
          .port(server.getAddress().getPort())
      .when().get("/orders/42")
      .then().statusCode(200)
          .contentType("application/json")
          .body("id", equalTo(42))
          .body("status", equalTo("draft"));
    } finally {
      server.stop(0);
    }
  }
}

Expect one passing case. Changing the expected order status to paid should produce a meaningful body assertion failure. The example demonstrates HTTP assertions, not a functioning order service, because a local fixture deliberately constructs the response.

Understanding the DSL chain

given defines request settings: URL, headers, body, and authentication. when performs the action. then verifies the response. This structure clarifies preconditions and outcomes but should not become a long sequence of unrelated checks.

Distinguish required contract fields from important business values. Comparing an entire JSON response literally can be brittle with timestamps and technical metadata. Checking only field presence is too weak: an order with another user's ownerId would pass.

Data setup and real operations

In a test service, create an isolated cart with two items costing 5,000 monetary units each. Apply an agreed 10% coupon. Expect a total of 9,000, your cart's identifier, and correct persisted state after rereading.

A negative case submits an unknown coupon and checks the specified error code, reason, and unchanged total. Derive statuses and rounding from the product contract. Accepting any 4xx error can hide an access error replacing the intended validation error.

Check ownership using separate clients for two roles. The owner reads the created order; another user requests the same ID and receives the specified denial without private data. The first user's next operation should still work. Alternating clients exposes mixed credentials or server context. Cleanup must use exact IDs and the test-data owner's rights rather than deleting all orders. For parallel execution, associate each resource with its run and verify setup before the main assertion.

RequestSpecification and reuse

RequestSpecification collects shared settings: base address, content type, safe headers, and filters. Keep technical configuration there and pass scenario data explicitly. This reduces repetition while keeping requests readable.

Mutable global configuration complicates parallel execution and role changes. Even if a version improves internal thread safety, shared credentials or data objects can still mix scenarios. Prefer explicit local specifications and isolated entities.

JSON Schema and data meaning

Schema checks help enforce required fields and types. They use the separate json-schema-validator module and a schema format supported by the selected version. Check capabilities before copying another project's schema.

A schema cannot establish correct pricing, access, or status transitions. Combine structural assertions with specific business expectations. For decimals, account for numeric representation and use a suitable type or matcher rather than arbitrary float equality.

Observing asynchronous outcomes

An accepted HTTP request may only queue a job. If the contract promises a later result, observe it through the agreed boundary: operation status, an event, or rereading. Bound the wait and retain the last response for diagnosis.

Do not create the order again during every polling iteration. Repeat a safe observation of the existing operation. Test idempotency separately through its intended mechanism instead of assuming it from the HTTP method.

Logs and investigation

A useful failure shows the method, safe address, expected and actual field values, response code, and operation ID. Failure-only logging reduces noise, but filters must remove tokens, cookies, and sensitive payloads.

If the server returns HTML instead of JSON, investigate routing, redirects, proxies, and authentication first. Parsing failure is a symptom. Changing the JSON path or accepting every content type can hide the underlying integration defect.

Common mistakes and practical lessons

Common problems include checking only status 200, sharing one order across the suite, hidden role changes, and treating successful deserialization as a correct response. Do not disable TLS validation indiscriminately; identify the certificate issue and use the agreed test trust store.

REST Assured provides a convenient language for HTTP checks. Reliability comes from clear contracts, independent data, and verified consequences. Start with a small reproducible scenario and expand according to service risks, keeping every assertion's purpose clear.

When adding a case, identify the new risk it controls: another successful request does not necessarily extend useful coverage.

Further reading