Articles
REST Assured Interview Questions: Request Specifications, JSON Assertions, and Test Isolation
Practice REST Assured interview questions with request specs, GPath and numeric assertions, shared-state failures, and an isolated local API test suite.
A REST Assured interview answer should explain the request a test actually sends, the response property it checks, and the state another test could change. A suite that passes alone but fails with other tests may have a configuration or data-ownership defect. Adding retries before identifying that defect can make the failure harder to understand.
This guide uses official project documentation and original local experiments. It is not a collection of reported company questions. Start with designing automated regression tests for an API, then use the code-reading cases below to make your answers specific.
What Belongs in a Request Specification?
A request specification describes request setup: where to send the request, which parameters or headers to include, and related configuration. A response specification describes reusable expectations. Official API behavior: RequestSpecBuilder supports building and combining request specifications; combining settings can overwrite some fields and merge others. Do not assume every property is simply appended or deeply copied. RequestSpecBuilder documentation
For an interview exercise, reuse a factory that creates a specification. Keep the test-specific path, actor, and payload explicit. The following helper is from our local Java fixture; port is the ephemeral port assigned to its HTTP server.
static RequestSpecification fresh(String path) {
return new RequestSpecBuilder()
.setBaseUri("http://127.0.0.1")
.setPort(port)
.setBasePath(path)
.build();
}
Each call creates a new object. That is a deliberately narrow isolation choice, not a claim that every builder, filter, or specification is thread-safe. If the factory attaches a shared mutable authentication filter, you have reintroduced shared state through a different path.
Use a response specification for genuinely common expectations, such as a known success status or media type. Keep the business assertion visible in the test. A helper named assertSuccess becomes misleading when its entire implementation only checks HTTP 200.
The design question is ownership: which settings are stable suite configuration, and which belong to one request or scenario? A shared base URL may be harmless when never changed; a shared “current user” is much more likely to connect otherwise independent tests.
The Local Fixture and What Was Actually Tested
Our experiment used Java 17, REST Assured 5.5.7, JUnit 5.11.4, and Maven 3.9.16. A local HTTP server bound to 127.0.0.1 on an available port. It returned the request path, the raw query string, a price, a payment-like status, and three item records. No real accounts, external API, or authentication credentials were involved.
The response fields used in the four reading cases were:
{
"price": 12.12,
"status": "pending",
"items": [
{"id": 1, "active": true},
{"id": 2, "active": false},
{"id": 3, "active": true}
]
}
Six JUnit checks completed with zero unexpected failures or errors. Two checks deliberately captured incorrect assertions, and another deliberately forced a shared-state collision. A green test report therefore means the experiment reproduced its expected behaviors; it does not mean the intentionally broken request pattern was correct.
The fixture is small enough to isolate client behavior. It does not model a real payment system, eventual consistency, TLS, or production authentication. Those boundaries make the observed results easier to interpret rather than less useful.
Code-Reading Case One: Why Does the Price Assertion Fail?
Read this assertion against the fixture response:
response.then().body("price", equalTo(12.12));
Observed result: it failed. Changing the expected value to 12.12f passed in the pinned default configuration. The visible decimal text was not the issue; the expected Java numeric type differed from the parsed value used by the matcher.
Official guidance: REST Assured documents this float-versus-double distinction and supports configuring JSON decimal values to return as BigDecimal. Its JSON path syntax uses Groovy GPath rather than Jayway JsonPath. Official usage guide
For the decimal configuration, our check used BIG_DECIMAL and compared against new BigDecimal("12.12"). Constructing from a string avoids introducing a binary floating-point approximation into the expected decimal. If numeric scale is not part of the contract, use an appropriate numeric comparison rather than accidentally making scale equality the requirement.
A weak repair converts both sides to strings until the test passes. A stronger explanation identifies the parser configuration, the expected type, and what equality should mean for this field. For money, also ask whether the API contract uses decimal values or integer minor units; that is a contract decision, not something the test should silently invent.
Code-Reading Case Two: What Does This GPath Select?
response.then().body(
"items.findAll { it.active }.id",
contains(1, 3)
);
Observed result: the assertion passed. The expression first selects active objects, then projects their IDs. It produces the ordered list [1, 3] for the fixture. The wrong answer [true, false, true] confuses the filtering condition with the projected property.
Now change the requirement: the API promises membership but does not promise order. An ordered matcher may overconstrain the contract. Choose an order-insensitive assertion if that is the actual requirement, while still checking duplicates or count where they matter.
Another useful follow-up is an empty result. If no records are active, does an empty list satisfy the requirement, or should at least one active record exist? An assertion that every returned item is valid can pass vacuously when nothing is returned. State population expectations separately from properties of each selected item.
Do not paste a Jayway filter into this expression merely because both libraries use the name JsonPath. Read the expression in the syntax of the library actually imported by the project.
Code-Reading Case Three: Does Combining Specs Preserve Both Values?
In our pinned fixture, the base specification contained query parameter tag=a. A second specification, using the same local port, supplied tag=b. The request applied them in that order:
given().spec(base).spec(extra)
.get()
.then().body("query", equalTo("tag=b"));
Observed result: the server received tag=b. Assuming it would receive tag=a&tag=b was incorrect for this specific merge path. This is why an interview answer should identify the method, parameter category, configuration, and version instead of claiming that “merge” means one universal behavior.
The experiment also exposed a separate setup mistake: building the second spec without the fixture port changed the effective connection settings and produced a connection refusal. Providing the intended port resolved that setup error before the query behavior could be examined.
Explain the two failures in order. A refused connection is not a JSON assertion problem because the expected response was never obtained. Once the request reaches the fixture, inspect the actual query and compare it with the API contract. If the contract requires multiple values, construct and verify that request explicitly; do not weaken the assertion to hide accidental configuration loss.
Code-Reading Case Four: Does HTTP 200 Mean Paid?
Suppose a response specification checks only status code 200. The fixture passes that check, but this assertion fails:
response.then().body("status", equalTo("paid"));
Observed result: the body is still pending. Transport success and business completion are separate facts. The test should not declare a completed payment because the server successfully returned a pending representation.
If a real API is asynchronous, decide what the current request promises. It may promise acceptance, not completion. A separate bounded observation step can test eventual completion if the contract supports it. Repeatedly resubmitting a creation request is a different action and can introduce duplicate side effects.
For an interview answer, name the evidence required at each boundary: accepted request, created resource identity, current state, and any final state the scenario is responsible for verifying. This makes the test useful when the response remains syntactically valid but the workflow is wrong.
Why Can Tests Pass Alone but Fail Together?
The original shared-state experiment used two workers and latches to force this ordering:
- Worker A assigned a specification for
/alicetoRestAssured.requestSpecification. - Worker B replaced that global specification with one for
/bob. - Worker A built its request using the now-current global setting.
Observed result: both requests reached /bob. The test captured that wrong routing deterministically. It was not a probabilistic stress run, and the paths represented simple fixture labels rather than real identities.
The repaired version passed a fresh specification directly to each request. Under two-worker concurrent execution, Alice reached /alice and Bob reached /bob. The critical change was ownership of per-request configuration, not a longer timeout.
Calling a global reset after a test can help a serial suite clean up, but it does not establish isolation while another test is actively using the same global state. A cleanup action can itself interfere with a concurrent request. Keep that distinction explicit when discussing framework design.
Official JUnit guidance: parallel execution has configurable execution modes and synchronization mechanisms for shared resources. Our fixture’s concurrency came from an explicit two-worker executor inside a test, not a claim that all JUnit methods ran in parallel. JUnit 5.11.4 parallel execution
Separate Configuration, Authentication, and Test Data
When a suite fails together, inspect three kinds of ownership. Configuration includes base paths, parameters, default specifications, and filters. Authentication includes tokens, cookies, and refresh state. Test data includes the records each case creates, reads, changes, and removes.
A fresh request object addresses only the first category if everything else is still shared. Two tests editing the same order can conflict even when their HTTP requests are built independently. Give each scenario a distinct resource identity and make cleanup target that identity, rather than deleting all records with a broad shared label.
For authentication, avoid a mutable global “last logged-in user.” If a fixture deliberately shares a stable credential, explain its scope and whether refresh behavior can change it. Do not log real tokens simply to demonstrate that headers differ; log redacted identity metadata or use a local fixture that contains no credentials.
A useful diagnostic record contains the test name, run identifier, method, intended actor, sanitized path and parameters, response status, and failed business assertion. That usually provides more actionable evidence than an unfiltered body dump. Add sensitive payload details only when they are necessary and handled appropriately.
Before proposing framework-wide changes, reproduce one failing pair. Run each case alone, run them in the problematic order, then force the suspected interleaving where practical. A minimal reproduction lets you distinguish the actual cause from unrelated instability.
What Would You Include in a Small API Test Suite?
Begin with one successful workflow, one meaningful rejection, and one state-isolation check. Assert the response contract and the business property each scenario owns. Avoid turning every test into a long end-to-end journey that depends on all earlier tests succeeding.
Use factories for request setup and test data, small helpers for repeated assertions, and explicit inputs for scenario-specific behavior. If a helper hides the resource ID or actor, it may make the test shorter while making its failure much harder to diagnose.
| Practice question | What to demonstrate |
|---|---|
| Design automated regression tests for an API | Separate contract coverage from implementation detail. |
| Implement CRUD API and tests | Give each scenario clear setup and cleanup ownership. |
| Write good tests and define integration tests | State which boundary the local server actually tests. |
| Debug and Harden a Ticketing Backend API | Follow evidence from request to business result. |
| Extend an Existing Endpoint Cache and Prove It with Tests | Isolate state so one case cannot supply another’s result. |
Rehearse API regression-test design by explaining one failure without changing the assertion first. Identify the request that was sent, the result that was expected, and the smallest experiment that separates a client mistake from a service defect.
Sources and Further Reading
- REST Assured official usage guide — GPath, numeric matching, configuration, and assertions.
- REST Assured 5.5.7 RequestSpecBuilder API — specification construction and merging.
- JUnit 5.11.4 user guide — execution modes and shared-resource synchronization.