JSON Validation in Cucumber with REST Assured

What Is JSON Validation?

JSON validation is the process of verifying that an API response contains the correct structure, fields, values, data types, and business rules. When an API returns JSON data, automation should confirm that the response is accurate, complete, meaningful, and aligned with the expected contract. It is not enough for an API to return a successful HTTP status code. The response body must also prove that the requested operation produced the correct result.

In Cucumber with REST Assured, JSON validation usually starts after a step sends an API request and receives a Response object. The validation layer then checks status code, response body fields, nested objects, arrays, generated IDs, Boolean values, numeric boundaries, null handling, error objects, and business conditions. REST Assured provides fluent body assertions, JsonPath extraction, Hamcrest matchers, POJO deserialization, and JSON Schema validation support.

In simple terms, JSON validation confirms that the API returned the correct JSON response, not just the correct HTTP status code. This distinction is important in interviews and real projects because many weak API tests pass even when the returned data is wrong. A response may say 200 OK or 201 Created while still containing missing fields, incorrect roles, empty names, invalid balances, wrong status values, or broken nested data.

Why JSON Validation Is Important

Consider a create-user API that returns a response with an ID, name, and job title. If the API returns status code 201, a shallow test may pass immediately. But if the returned ID is null, the name is empty, or the job is wrong, the behavior is defective. The API may have accepted the request, but it did not produce the expected result. JSON validation catches this difference.

{
  "id": 101,
  "name": "John",
  "job": "QA Engineer"
}

If the response changes to the following body, a status-code-only test may still pass if the HTTP status remains 201 Created.

{
  "id": null,
  "name": "",
  "job": "Developer"
}

This is why JSON validation is mandatory for useful API automation. It proves that the response data matches the business expectation. It also protects downstream workflows. If an ID is missing from a create response, later update, retrieve, or delete calls may fail. Catching the problem at the first response makes debugging easier.

Response Validation Flow

A practical API validation flow usually starts by sending a request and receiving a response. The first check is often the HTTP status code because it confirms the broad outcome. After that, the automation validates JSON structure, required fields, field values, data types, nested objects, arrays, and business rules. If needed, it extracts values for later steps.

Send request
  -> Receive response
  -> Validate status code
  -> Validate JSON structure
  -> Validate JSON values
  -> Validate business rules
  -> Extract reusable data

This sequence keeps troubleshooting clear. If the status code is wrong, the request may have failed before the expected business response was produced. If the status code is correct but the JSON structure is wrong, the API contract may have changed or broken. If the structure is correct but the values are wrong, the business logic may be defective.

Example JSON Response

A simple JSON response may contain fields with different data types. A user response can include a numeric ID, string values, and a Boolean flag. Each type requires the correct validation approach. The framework should confirm that required fields exist, values match expectations, and dynamic fields follow valid rules.

{
  "id": 101,
  "name": "John",
  "job": "QA Engineer",
  "active": true
}

Automation should validate field existence, field values, data types, required fields, and business logic. For example, ID should not be null, name should match the created user, job should match the request or business rule, and active should have the expected Boolean value. For generated fields, the test should avoid hardcoding values that change on every run.

Response Object

REST Assured stores the API response in a Response object. This object is the starting point for JSON validation. It contains the response status, headers, body, cookies, content type, response time, and extraction methods. In a Cucumber framework, the response object is often stored in scenario context so multiple Then steps can validate different parts of the same response.

Response response;

Response storage should be scoped safely. Avoid global static response variables that can be overwritten during parallel execution. A scenario-scoped context object is a cleaner choice. The When step sends the request and stores the response. Then steps retrieve the response and validate status, body, schema, or extracted values.

Basic JSON Validation

Basic JSON validation uses REST Assured's body() method and Hamcrest matchers. The first argument is usually a JsonPath expression, and the second argument is the expected condition. For a simple top-level field, the path is just the field name.

response.then()
    .body("name", equalTo("John"));

This assertion validates that the JSON field named name has the value John. The required matcher is commonly imported statically.

import static org.hamcrest.Matchers.equalTo;

This kind of assertion is readable and effective for simple response bodies. In larger frameworks, repeated validations should be moved into response validator classes so step definitions do not become cluttered with assertion details.

Multiple Field Validation

REST Assured allows multiple JSON body validations to be chained. This is useful when a scenario needs to check several fields from the same response. The chain remains readable when the validations are closely related to one behavior.

response.then()
    .body("name", equalTo("John"))
    .body("job", equalTo("QA Engineer"))
    .body("active", equalTo(true));

Multiple field validation should still be purposeful. Do not validate every field in every scenario just because the response contains many fields. If the scenario is about creating a user, validate fields that prove the user was created correctly. If the scenario is about account status, validate the status-related fields. Focused validation keeps tests meaningful and easier to maintain.

Validate Field Exists

Generated IDs, timestamps, tracking numbers, and correlation values are often dynamic. The test usually should not expect a hardcoded value. Instead, it should validate that the field exists and is not null. REST Assured can use notNullValue() for this purpose.

response.then()
    .body("id", notNullValue());
import static org.hamcrest.Matchers.notNullValue;

Field-existence validation is useful for generated IDs because the exact value may change every run. The test can validate that the ID exists, then extract it for later steps. If business rules define a format, range, or prefix, validate that rule instead of a fixed literal value.

Validate String Values

String validation can check exact matches, partial matches, prefixes, suffixes, and patterns depending on the API contract. Exact matching is useful when the value is stable. Partial matching is useful when messages include dynamic content such as IDs, usernames, or dates.

response.then()
    .body("message", containsString("Success"));

Other useful string matchers include startsWith() and endsWith(). Use exact matching for fields that should not vary. Use contains or pattern-based validation for dynamic messages. Be careful with overly broad string assertions because they may pass even when important details are wrong.

Validate Boolean Values

Boolean fields should be validated as Boolean values, not as strings. A JSON value of true is different from a string value of "true". REST Assured can validate Boolean fields directly.

response.then()
    .body("active", equalTo(true));

Boolean validation is common for flags such as active, enabled, verified, deleted, locked, subscribed, or default. These fields often represent important business state. If an account should be inactive after deletion, the API response should prove that state accurately.

Validate Numbers

Numeric validation can check exact values, ranges, minimums, maximums, and comparisons. REST Assured works well with Hamcrest matchers such as greaterThan(), lessThan(), greaterThanOrEqualTo(), and lessThanOrEqualTo(). This is useful for IDs, counts, balances, totals, page numbers, limits, and prices.

response.then()
    .body("id", greaterThan(0));

For business fields such as account balance or order total, range validation may be more meaningful than exact validation. For example, account balance should not be negative unless the product allows overdraft. A cart total should equal the sum of line items, tax, discount, and shipping. Numeric checks often become business-rule checks.

Validate Null and Non-Null Values

Some fields are expected to be null under specific conditions. Others must never be null. REST Assured supports both nullValue() and notNullValue(). These checks are important because null handling often reveals contract and serialization issues.

response.then()
    .body("middleName", nullValue());
response.then()
    .body("middleName", notNullValue());

Null expectations should be based on the API contract. If optional fields are omitted instead of returned as null, the validation should reflect that behavior. Teams should clarify whether absent and null mean the same thing in the API. In many contracts, they are different.

Validate Nested JSON

Many real API responses are nested. A customer may contain an address, preferences, account status, and contact details. An order may contain customer data, items, shipping details, taxes, and payment information. REST Assured uses dot notation to access nested fields.

{
  "user": {
    "name": "John",
    "email": "john@test.com"
  }
}
response.then()
    .body("user.name", equalTo("John"));

Nested validation should be organized carefully. If nested checks are repeated across many scenarios, move them to reusable validator methods. This avoids scattering paths such as customer.address.city across many step definitions. Centralized paths are easier to update if the API structure changes.

Validate JSON Arrays

JSON arrays are common in list, search, and collection endpoints. A response may return users, products, orders, transactions, comments, notifications, or permissions. REST Assured can validate array size, item values, and fields inside array objects.

{
  "users": [
    { "name": "John" },
    { "name": "David" }
  ]
}
response.then()
    .body("users.size()", equalTo(2));

Array validation should avoid unstable assumptions. If the data source changes frequently, exact size checks can become brittle. For controlled test data, exact size may be appropriate. For broader search results, validate that expected items are present or that each item follows a rule.

Validate Array Contains Items

REST Assured can validate that an array contains one or more expected values. This is useful when the order of items is not important. For example, a list of user names may need to include John, or a permissions list may need to include READ and WRITE.

response.then()
    .body("users.name", hasItem("John"));
response.then()
    .body("users.name", hasItems("John", "David"));

Use hasItem() and hasItems() when membership matters more than position. If order matters, use assertions that check the exact list or specific indexes. The validation should match the API contract. Some APIs guarantee sorted order; others do not.

Validate Collection Size and Empty Collections

Collection size validation is useful for endpoints where the expected number of returned records is controlled. REST Assured can validate size with hasSize(). It can also validate empty collections with empty(). These checks are common for search, filter, pagination, and permission APIs.

response.then()
    .body("users", hasSize(5));
response.then()
    .body("users", empty());

Empty collection validation is especially important. A well-designed API may return an empty array when no records match, not a null value or a server error. Validating this behavior improves consumer confidence and catches regression defects in list endpoints.

Extract Values Using JsonPath

JsonPath extraction allows the framework to read values from a JSON response. Extracted values can be used for later requests, cleanup, reporting, or additional assertions. REST Assured provides convenient methods such as getString(), getInt(), and getBoolean().

String id = response.jsonPath().getString("id");
int numericId = response.jsonPath().getInt("id");
boolean active = response.jsonPath().getBoolean("active");

Extraction should be done after validating that the field exists. If a missing ID is extracted and used in another request, the next failure may be confusing. First prove that the response contains the required value, then store or reuse it. This small discipline makes API workflow failures much easier to understand.

Store Values for Later Steps

In Cucumber, values extracted from a response are often stored in scenario context. For example, a create customer response may return a customer ID. Later steps can use that ID to retrieve, update, or delete the customer. This allows one scenario to validate a complete behavior flow without relying on external state.

String customerId =
    response.jsonPath().getString("id");

context.setCustomerId(customerId);

Scenario context should be scoped to the current scenario. Avoid unsafe static variables when tests may run in parallel. Each scenario should have its own context so values do not leak across tests. This is important for reliable CI execution.

Deserialize JSON to POJO

Deserialization converts JSON into a Java object. Instead of validating every field with raw JsonPath strings, the framework can map a response into a POJO and use normal Java getters. This improves readability for complex responses and makes validators easier to write.

UserResponse user =
    response.as(UserResponse.class);

assertEquals("John", user.getName());
assertTrue(user.isActive());

POJO mapping is helpful when the response structure is stable and reused often. It provides a clearer model of the response contract. However, JsonPath is still useful for quick validations, dynamic responses, partial checks, and cases where building a full POJO is unnecessary. A practical framework can use both approaches.

Validate Response Schema

JSON Schema validation checks whether the response structure matches an expected contract. Instead of validating every field manually, a schema can define required fields, field types, allowed values, nested structure, arrays, and constraints. REST Assured can validate a response against a schema file.

response.then()
    .body(matchesJsonSchemaInClasspath("schemas/user-schema.json"));

Schema validation is valuable because it detects contract changes. If a required field disappears, a type changes from number to string, or a nested structure changes unexpectedly, the schema check can fail quickly. Schema validation does not replace business validation. It complements it. Schema checks confirm structure; business assertions confirm meaning.

Business Rule Validation

Business rule validation is where API automation becomes more valuable than simple contract checking. A response may have the right structure and still violate a rule. For example, an account balance should not be negative, an order total should match line items, an inactive user should not receive an active session, and a rejected payment should not create a completed order.

response.then()
    .body("accountBalance", greaterThanOrEqualTo(0));

Business validations should be expressed clearly in feature files and implemented carefully in validator classes. The Gherkin step may say, "Then the account balance should remain valid." The validator can check the exact numeric rule. This keeps the scenario readable while proving the important logic.

Combining Validations

A single response can be validated across status code, required fields, exact values, Boolean flags, and string patterns. REST Assured supports fluent chaining, which keeps related validations together. This is useful when all checks support the same scenario outcome.

response.then()
    .statusCode(200)
    .body("id", notNullValue())
    .body("name", equalTo("John"))
    .body("active", equalTo(true))
    .body("email", containsString("@"));

Even when validations are combined, avoid creating unreadable assertion chains. If a response has many rules, use a validator method with a meaningful name such as verifyUserCreatedSuccessfully(). The method can contain all detailed assertions while the step definition remains short.

Validation Flow in Cucumber

In a Cucumber framework, JSON validation should be layered. The feature file describes expected behavior. The step definition receives the Cucumber step. The response validator performs assertions. REST Assured and JsonPath provide the technical validation tools. Reports show which scenario and step passed or failed.

Feature File
  -> Step Definition
  -> Response Validator
  -> REST Assured
  -> JSON Response
  -> Assertions

This design keeps step definitions clean. A step definition should not contain dozens of repeated body assertions. It should call a validator method that clearly explains what is being checked. This makes the framework easier to maintain and review.

Common Hamcrest Matchers

Hamcrest matchers cover most day-to-day JSON validation needs in REST Assured. Common matchers include equalTo(), notNullValue(), nullValue(), containsString(), startsWith(), endsWith(), hasItem(), hasItems(), hasSize(), greaterThan(), lessThan(), everyItem(), and empty().

Knowing these matchers helps in interviews and practical work. They allow tests to express precise expectations without writing large amounts of custom assertion code. The key is to choose the matcher that reflects the business rule. Exact value, range, non-null, collection membership, and empty response are different expectations and should be tested differently.

Validate Error Responses

JSON validation must include negative responses. Error contracts are part of API quality. When a request is invalid, unauthorized, forbidden, duplicated, or missing required data, the API should return a predictable JSON error response. This response may include error code, message, field name, timestamp, trace ID, or validation details.

response.then()
    .statusCode(400)
    .body("error", equalTo("Invalid Request"));

Testing error responses prevents vague failures in client applications. If an API suddenly changes an error code or removes a message field, consumers may break. Negative validation should be treated with the same seriousness as positive validation.

Common Mistakes

The most common mistake is validating only the status code. A test that checks only statusCode(200) does not prove that the JSON content is correct. Always validate response content as well. Another mistake is hardcoding dynamic values such as generated IDs. If the API generates IDs dynamically, validate that the ID is not null or follows the expected rule instead of expecting a fixed number.

Teams also ignore nested objects. They validate only top-level fields while defects hide inside nested customer addresses, payment details, permissions, or item lists. Duplicate validation code is another problem. Repeating the same assertions across step definitions makes maintenance expensive. Create reusable validator methods.

Another frequent mistake is not testing negative responses. Successful responses are important, but error responses prove that the API handles invalid conditions correctly. A strong suite validates both.

Best Practices

Validate the response body in addition to the HTTP status code. Validate field existence, values, data types, nested structures, arrays, and business rules. Use JsonPath for extracting reusable values. Use POJOs for complex response handling when the structure is stable. Use JSON Schema validation for contract verification. Centralize common validations in reusable validator classes.

Validate both successful and error responses. Avoid hardcoded expectations for dynamic values. Keep Cucumber step definitions clean by delegating detailed assertions to validators. Store extracted IDs and other reusable values in scenario context. Keep validation names meaningful so failures are easy to understand in reports.

Enterprise Framework Architecture

An enterprise Cucumber and REST Assured framework usually separates validation responsibilities clearly. The feature file describes the behavior. The step definition maps Gherkin to Java. The API service sends the request. REST Assured receives the response. The response validator checks JSON fields, JsonPath expressions, POJOs, or schema files. Scenario context stores extracted values. Reports show the outcome.

Feature File
  -> Step Definition
  -> API Service
  -> REST Assured
  -> Response Validator
  -> JsonPath / POJO / JSON Schema
  -> Report

This structure supports maintainability. If a response field changes, update the validator or POJO. If a schema changes, update the schema file. If the endpoint changes, update the API service. The feature file should change mainly when behavior changes.

JSON Validation vs JSON Schema Validation

JSON validation and JSON Schema validation complement each other. JSON validation checks field values and business rules. JSON Schema validation checks response structure, required fields, and data types. JSON validation usually uses REST Assured body assertions and Hamcrest matchers. Schema validation uses a schema file that describes the contract.

JSON ValidationJSON Schema Validation
Validates field valuesValidates response structure
Checks business rulesChecks API contract
Uses Hamcrest matchersUses JSON Schema
Verifies dynamic contentVerifies required fields and data types
Used in almost every API testCommonly used for contract testing

For example, schema validation can prove that an order response contains an orderId, status, total, and items array. JSON validation can prove that the total is correct, the status is confirmed, and the item names match the request. Both checks matter.

Real-Time Example

Imagine an order API. The client creates an order with two products. The API returns a JSON response containing order ID, customer ID, item list, subtotal, tax, shipping, total, payment status, and order status. A shallow test may check only 201 Created. A strong test validates that order ID is present, status is Created, payment status is Pending or Paid based on the scenario, item count is two, item names match the request, and total equals the expected amount.

If the response contains a wrong total, missing item, incorrect status, or null order ID, JSON validation catches the defect immediately. The test may also extract the order ID and use it to retrieve the order in a later step. This validates both creation and retrieval behavior while keeping data connected through scenario context.

Validating Dynamic Fields

Many JSON responses contain dynamic fields that change on every execution. Generated IDs, timestamps, request IDs, correlation IDs, session IDs, token expiry values, and calculated totals may not be known before the request is sent. These fields should not be validated with fixed hardcoded values unless the test data is fully controlled and the value is intentionally predictable.

For dynamic fields, validate the rule instead of the exact value. A generated ID may need to be non-null and greater than zero. A timestamp may need to exist and follow an expected format. A correlation ID may need to be returned in both the response body and headers. A calculated total may need to equal the sum of known line items. This approach keeps tests stable while still proving correctness.

Validating Optional Fields

Optional fields require careful thinking. Some APIs return optional fields as null. Some omit optional fields completely. Some return empty strings, empty arrays, or default values. The automation should match the API contract instead of assuming one style. If the contract says middle name is omitted when not supplied, a null assertion is not correct. If the contract says middle name is present with null value, then absence may be a defect.

Cucumber scenarios should describe the business expectation clearly. For example, "Then the optional referral code should not be returned" is different from "Then the referral code should be null." The validator can implement the exact JSON check. This distinction prevents misleading tests and makes contract behavior clear to API consumers.

Validating Lists with Filters

Search and list APIs often return collections where every item must satisfy a filter. For example, a product search for category "Books" should not return electronics. A transaction search for a date range should not return records outside that range. A customer search for active users should not return inactive users. These validations are stronger than simply checking that the response contains at least one item.

REST Assured and JsonPath can extract arrays and fields from arrays, while Java assertions can verify every item. In a clean framework, this logic belongs in a response validator. The feature file can say that all returned products should belong to the requested category. The validator can inspect each object and fail with a useful message if one item violates the rule.

Validating Pagination Responses

Pagination responses usually contain both data and metadata. The data may be an array of records. The metadata may include page number, page size, total records, total pages, sort order, next page availability, or links. JSON validation should check both parts when pagination behavior matters. A response that returns the correct records but wrong page metadata can still break client applications.

Pagination tests should be designed with stable data. If the total number of records changes frequently, exact count assertions may become brittle. In controlled environments, create known data before the test and clean it after. In shared environments, validate safer rules such as page size limits, non-negative totals, expected page number, and consistency between returned item count and page metadata.

Validating Date and Time Fields

Date and time fields are common sources of API defects. Time zones, formats, milliseconds, daylight-saving behavior, and server clock differences can all affect responses. A JSON validation strategy should define what is expected. For example, timestamps may need to follow ISO-8601 format, use UTC, or appear within a reasonable time window after the request.

Avoid fragile exact timestamp checks unless the value is deterministic. If a createdAt field is generated by the server, validate that it is present, parseable, and close to the current execution time within a reasonable tolerance. If the API returns date-only values, validate the expected date format and business rule. This gives reliable coverage without creating time-based flakiness.

Validating Error Arrays

Validation APIs often return an array of errors when multiple fields are invalid. For example, a create-user request may fail because email is missing, password is too short, and phone number has invalid format. A strong test should validate that the response contains the expected error entries, not only that the response status is 400.

Error arrays should usually be validated by code or message rather than relying only on order. Unless the API contract guarantees error order, tests should check that required errors are present. This avoids brittle failures when the server changes validation order but still returns the correct error information. For business-critical APIs, validating error codes is often more stable than validating full message text.

Designing Validator Classes

Validator classes keep JSON validation logic reusable and readable. Instead of placing many body() assertions inside step definitions, create classes such as UserResponseValidator, OrderResponseValidator, PaymentResponseValidator, and ErrorResponseValidator. Each class can provide meaningful methods that describe the validation purpose.

A method named verifyUserCreatedSuccessfully() is easier to understand than a block of repeated JSONPath assertions inside a step definition. It also gives one place to update if the response contract changes. Good validator classes make the framework easier to maintain, reduce duplicated assertions, and keep Cucumber glue code focused on connecting Gherkin to automation behavior.

Interview-Ready Summary

JSON validation verifies that an API response contains the expected fields, values, data types, nested structures, arrays, and business logic. REST Assured supports JSON validation using Hamcrest matchers and JsonPath. Complex responses can be deserialized into POJOs for easier validation. JSON Schema validation ensures that the response structure matches the expected API contract.

In enterprise API frameworks, validation logic should be centralized into reusable validator classes. Step definitions should stay thin. Both positive and negative responses should be validated. Dynamic values should not be hardcoded. Extracted values should be stored in scenario context only after they are validated.

Golden Rules

Never validate only the HTTP status code; always validate the JSON response. Validate field existence, values, data types, nested objects, arrays, and business rules. Use JsonPath to extract reusable response values. Use POJOs and JSON Schema validation for complex APIs. Centralize reusable validation logic to keep step definitions clean and maintainable.

The practical takeaway is simple: status code tells you whether the API call broadly succeeded or failed, but JSON validation tells you whether the API returned the right data. Strong API automation needs both.