Prepare for Rest Assured interview questions grouped by experience level.
Rest Assured Interview Question & Answers
0-2 Years
Rest Assured is a Java library used to test RESTful APIs, providing a domain-specific language that lets you write API test cases in a readable, given-when-then style without needing to write raw HTTP client code.
The given-when-then syntax structures a test into three parts: given() sets up preconditions like headers and parameters, when() specifies the HTTP action like a GET or POST request, and then() defines the assertions on the response.
You add the rest-assured dependency to the pom.xml file's dependencies section, specifying the group ID io.rest-assured, the artifact ID rest-assured, and the version you want to use.
A basic GET request looks like given().when().get("/users").then().statusCode(200);, where given() sets up any needed configuration, when().get() sends the request, and then().statusCode() validates the response status.
You use the statusCode() method inside the then() block, such as .then().statusCode(200), to assert the response returned the expected HTTP status code.
You use the queryParam() method within the given() block, such as given().queryParam("id", 5), to attach query parameters to the request URL.
You use the pathParam() method along with a placeholder in the endpoint URL, such as given().pathParam("id", 5).when().get("/users/{id}"), to substitute a value into the URL path.
You use the header() method within the given() block, such as given().header("Authorization", "Bearer token"), to attach custom headers to the outgoing request.
You use the body() method within the given() block, such as given().body(jsonPayload).when().post("/users"), passing either a JSON string or a serialized Java object as the request body.
You use the body() assertion inside then(), combined with a JSON path expression, such as .then().body("name", equalTo("John")), to check that a specific field in the response matches an expected value.
JsonPath is the expression syntax Rest Assured uses to navigate and extract values from a JSON response body, similar to how XPath works for XML, letting you reference nested fields like "user.address.city".
You use the extract() method after then(), such as .extract().path("id"), to pull a specific value out of the response so it can be reused, for example as input to a subsequent request.
A RequestSpecification defines a reusable set of request configuration, like base URI, headers, and content type, that can be applied across multiple tests without repeating the same setup code each time.
A ResponseSpecification defines a reusable set of expected response validations, like status code and content type, that can be applied across multiple tests to avoid duplicating the same assertion logic.
You set RestAssured.baseURI to the desired URL, typically once in a setup method, so individual test methods don't need to repeat the full URL for every request.
You use the contentType() assertion inside then(), such as .then().contentType("application/json"), to confirm the response's Content-Type header matches what's expected.
Rest Assured supports all standard HTTP methods, including GET, POST, PUT, DELETE, PATCH, HEAD, and OPTIONS, each available as a corresponding method like get(), post(), put(), and delete().
You use the formParam() method within the given() block, such as given().formParam("username", "john"), to send data as application/x-www-form-urlencoded content.
You use methods like .log().all() within given() to log the request, or .then().log().all() to log the response, printing details like headers and body to help debug a failing test.
RestAssured.get() is a static convenience method for a simple GET request without additional configuration, while given().get() lets you chain request setup, like headers and parameters, before sending the GET request.
You chain multiple assertion calls together, such as .then().statusCode(200).body("name", equalTo("John")).body("age", equalTo(30)), or use the assertThat() and and() methods to make the chain more readable.
Serialization is the process of converting a Java object into a format like JSON that can be sent as a request body, which Rest Assured handles automatically when you pass a POJO to the body() method with an appropriate object mapper on the classpath.
Deserialization is the process of converting a JSON or XML response body back into a Java object, which Rest Assured supports through the as() method, such as response.as(User.class).
You configure timeout settings through the RestAssuredConfig object with HttpClientConfig, setting properties like connection timeout and socket timeout before applying that config to your request specification.
You use the header() assertion inside then(), such as .then().header("Content-Type", "application/json"), to confirm a specific response header matches the expected value.
RestAssured.reset() resets any static configuration, like base URI or default request specification, back to its default state, commonly used between test classes to avoid configuration leaking across unrelated tests.
You use the auth().basic() method within given(), such as given().auth().basic("username", "password"), to attach basic authentication credentials to the request.
You structure it similarly to a POST request, using given().body(payload).when().put("/users/1"), where the body contains the updated resource data being sent to the endpoint.
statusLine() lets you assert on the full HTTP status line text, such as "HTTP/1.1 200 OK", which can be useful when you need to validate more than just the numeric status code.
You can use JsonPath expressions with array indexing or size checks, such as .body("items.size()", equalTo(3)) or .body("items[0].name", equalTo("first item")), to validate array contents in the response.
RestAssuredMockMvc is an extension that lets you use Rest Assured's syntax to test Spring MVC controllers directly through MockMvc, without needing to start an actual running server, which speeds up test execution.
You can use the headers() method with a Map of header names and values, such as given().headers(headerMap), to attach several headers to the request in a single call rather than chaining multiple header() calls.
Hamcrest is a matcher library that Rest Assured integrates with for writing assertions, providing readable matcher methods like equalTo(), containsString(), and hasSize() that make validation statements read closer to natural language.
You call given().relaxedHTTPSValidation() to bypass strict SSL certificate checks, which is useful for testing against environments using self-signed certificates but shouldn't be used against production.
You can check that a JsonPath expression for that field returns null, such as .body("deletedField", nullValue()), to confirm the field isn't present in the response.
given().contentType() sets the Content-Type header of the outgoing request, telling the server what format the request body is in, while then().contentType() asserts what Content-Type the server's response actually returned.
3-6 Years
I would create a shared RequestSpecBuilder that sets the base URI, common headers like Content-Type, and any shared authentication, then reuse that spec across test classes rather than repeating the same setup in every test method.
I would extract the needed value using .extract().path() from the first response, store it in a variable, then use it as a path parameter or request body field in the next request, keeping the chain explicit and readable rather than relying on shared mutable test state.
I would first make a request to the authentication endpoint to retrieve a valid token, extract it from the response, then include it as a Bearer token in the Authorization header for subsequent requests using given().header("Authorization", "Bearer " + token).
I would use JsonPath expressions targeting the specific nested fields I need to validate, and for deeply nested or repetitive structures, consider deserializing the response into a POJO and asserting on the object's fields directly, which can be more readable than long JsonPath chains.
I would use the testing framework's parameterized test feature, like TestNG's @DataProvider, to supply different input values and expected results to the same test method, avoiding duplicated test methods that only differ in their input data.
I would write conditional assertions or separate test cases for each expected schema variation, and consider using JSON schema validation to confirm the overall structure matches the expected shape for that specific scenario rather than asserting on every field individually.
I would use the time() method available on the response, such as response.time(), to capture the response duration and assert it falls under an acceptable threshold, though I'd treat this as a smoke check rather than a substitute for dedicated performance testing tools.
I would use the multiPart() method within given(), passing the file and its parameter name, such as given().multiPart("file", fileObject), to send a multipart form request matching what a real file upload would look like.
I would centralize common configuration, like base URI and default headers, into a shared base test class or a static configuration method run once, and use RequestSpecBuilder and ResponseSpecBuilder for specs reused across multiple endpoint test classes.
I would use the matchesJsonSchemaInClasspath() assertion, pointing to a schema file, to validate the overall response structure matches expectations, which catches structural drift, like a missing or renamed field, that individual field assertions might miss.
I would write explicit negative test cases sending malformed or missing data, asserting the API returns the correct error status code, like 400, and that the error response body contains a meaningful, well-structured error message rather than a generic failure.
I would externalize the base URI and other environment-specific values into a properties file or environment variables, selected at runtime based on a system property, rather than hardcoding URLs directly in test code.
I would send requests with different page and size parameters, asserting the returned item count matches the requested page size and that different pages return non-overlapping, correctly ordered data, along with checking boundary cases like requesting a page beyond the available data.
I would use a Hamcrest matcher like notNullValue() or a pattern matcher for dynamic values rather than an exact equalTo() match, since asserting an exact dynamic value would make the test brittle and likely to fail on every run.
I would validate the API response through Rest Assured first, then run a direct database query afterward to confirm the underlying data was actually persisted or changed as expected, since the API response alone doesn't guarantee the backend state is correct.
I would send requests in a tight loop until the rate limit threshold is crossed, then assert the subsequent request returns a 429 status code along with any expected rate limit headers, like a retry-after value.
I would write an explicit negative assertion checking that the response body's JsonPath for that field returns null or that the field is absent entirely, since silently passing tests that never check for a field's absence can let a sensitive data leak go unnoticed.
I would parameterize the Accept header across test runs and use Rest Assured's built-in XmlPath alongside JsonPath depending on the response format, asserting the same underlying data is present correctly in both representations.
I would use a polling approach with Awaitility or a custom retry loop that re-sends the request until the expected condition is met or a timeout is reached, rather than adding a single arbitrary sleep, since eventual consistency delays can vary and a fixed sleep is either wasteful or unreliable.
I would use the cookie() assertion inside then(), such as .then().cookie("session", notNullValue()), to confirm a specific cookie is present in the response with the expected value or a valid non-null value.
I would use assertThat() with soft assertions where the library supports it, or split overly broad tests into more granular test methods, so that a failure in one assertion doesn't prevent other independent checks in the same test run from reporting their own results.
I would first make a GET request to retrieve the CSRF token, often from a response header or cookie, extract it, and include it in the headers of the subsequent state-changing request, since submitting without it would correctly fail with a security error.
I would run the test suite as part of the build pipeline against a deployed test environment, configuring the base URI dynamically based on the pipeline's target environment, and fail the build if any API test fails, treating these as a gate before promoting a deployment.
I would use JsonPath to extract the full list and assert it matches an expected ordered list using a Hamcrest contains() matcher, rather than checking individual indices separately, since contains() validates both the values and their exact order in one assertion.
6-8 Years
I would build a pluggable authentication layer where each service's auth strategy, like OAuth2, API key, or basic auth, is encapsulated behind a common interface, so test classes don't need to know the specific auth mechanism details of the service they're testing, only which strategy to apply.
I would profile which tests take disproportionately long, often due to unnecessary setup or serial execution of independent tests, and look at whether tests can run in parallel safely, or whether some are actually better suited to lower-level unit or contract tests instead of full API round-trip tests.
I would design tests to create their own isolated test data at the start and clean it up afterward, rather than relying on pre-existing shared fixtures, since shared mutable fixtures across a growing test suite tend to cause flaky failures when tests run in parallel or in an unexpected order.
I would use Rest Assured for testing the service's actual behavior end-to-end, and pair it with a dedicated contract testing tool for verifying the API meets the specific expectations of each consumer, since contract tests catch breaking changes for consumers earlier and more precisely than integration tests alone.
I would check for shared mutable state between tests running in parallel, timing-sensitive assertions against eventually consistent backend data, or environment-specific issues like DNS or connection pool exhaustion under load, since intermittent API test failures are rarely about the library itself and usually about test isolation or environment stability.
I would maintain a Rest Assured test suite specifically targeting the previous API version's contract, run against the new deployment, so any breaking change to fields or behavior that existing consumers depend on gets caught before it reaches production.
I would build a custom filter when a concern, like adding a consistent request signature or capturing detailed logs, needs to apply uniformly across many different test classes, since a filter centralizes that logic in one place rather than duplicating it in every test's given() block.
I would test the gateway's routing and transformation logic specifically, separately from testing the correctness of each backend service's own business logic, since conflating the two makes it hard to tell whether a failure is a gateway configuration issue or a genuine backend defect.
I would fire the same request concurrently from multiple threads using Rest Assured's underlying HTTP client and assert that the resulting state and response are consistent regardless of how many times or how closely together the identical request was sent.
Testing against a real deployed environment catches integration issues a mock can't, but it's slower and more prone to environmental flakiness, while mocking is fast and stable but risks the mock drifting out of sync with real backend behavior. I would use both, reserving full environment tests for critical paths and mocks for broader, faster coverage.
I would invest in clear, descriptive test and assertion naming along with generating a readable test execution report, since raw Rest Assured code isn't accessible to non-technical stakeholders, but a well-structured report summarizing what passed and failed in business terms can be.
I would look for shared static state, like a static RequestSpecification being mutated by one test and affecting another, or test execution order dependencies, since tests that pass in isolation but fail together almost always point to some form of unintended coupling between tests rather than a Rest Assured configuration issue.
8-10 Years
I would define a shared baseline, like a common base test class for configuration and a consistent pattern for request and response specs, while leaving room for teams to add service-specific extensions, since forcing complete uniformity across genuinely different services tends to create awkward workarounds.
I would look at how often integration issues between services are discovered late, closer to production, despite passing API tests, since that pattern usually signals the organization has outgrown integration testing alone and would benefit from contract testing catching consumer-provider mismatches earlier in the pipeline.
I would quantify the duplicated effort teams are already spending building similar request and response handling logic independently, since that redundant cost, made visible, usually justifies centralizing shared framework investment more clearly than an abstract consistency argument.
I would classify failures by which business-critical flows they protect, prioritizing failures in flows like payment or authentication for immediate escalation, while allowing lower-risk endpoint failures to follow a standard, less urgent triage process.
I would establish a minimum coverage expectation for any service exposed to external or cross-team consumers, since inconsistent coverage across services that other teams depend on creates hidden risk that isn't visible until an untested path causes a production incident.
I would provide a shared library encapsulating common authentication and data setup patterns that teams can adopt, rather than mandating a single rigid implementation, since teams with genuinely different auth requirements need enough flexibility to extend the shared pattern rather than fight against it.
I would tie test suite updates to the same release process as the API change itself, requiring updated or new tests as part of the same pull request rather than allowing test suite updates to lag behind, since a stale test suite gives false confidence that the API still works as originally tested.
I would weigh the maintenance savings of consolidation against the migration cost and the risk of forcing genuinely different testing needs into one inflexible framework, generally favoring consolidation only when the frameworks are already solving very similar problems in slightly different ways.
I would separate a fast-running smoke suite covering critical paths that runs on every commit from a fuller suite that runs less frequently, like nightly, since forcing every commit to wait on the fuller suite slows delivery without proportional benefit for most changes.
I would define a small set of shared metrics, like pass rate trends and time-to-fix for failing tests, reported consistently across teams, while allowing supplementary team-specific detail, so leadership gets a comparable view of API quality without forcing every team into an identical reporting format that doesn't fit their context.
I would weigh the value against how often stakeholders outside the immediate testing team actually need visibility into API test health, since a dashboard is worth building when it replaces recurring manual status reporting, but adds unnecessary maintenance overhead if only the testing team itself ever looks at the results.
I would retain enough history to identify meaningful flakiness or degradation trends, typically several months, while balancing storage cost, since very old execution data rarely informs current decisions once the underlying API and test suite have changed substantially since then.
I would quarantine a test showing a clear pattern of environment-related flakiness rather than API defects, tracking it for a fix, while still blocking on tests failing consistently, since treating every flaky failure as blocking erodes trust in the CI signal and encourages teams to ignore failures altogether.
I would point to specific incidents where a functionally correct API change caused a production performance regression that functional tests alone wouldn't have caught, since that concrete gap makes the case for additional performance-aware checks clearer than a general argument about testing thoroughness.
10+ Years
I have them think through how a test would need to change if the API added a new required field or changed an existing one, since that exercise reveals whether their current test structure is resilient or brittle. I also review their specs for duplicated setup code, since that's usually the first sign a shared spec or base class is needed.
I would start by cataloging where inconsistent API testing practices have caused real friction, like duplicated framework code across teams or gaps in coverage that led to production incidents, then build shared tooling and training addressing those specific pain points rather than mandating a theoretical best-practices document.
I frame it in terms of a specific past incident that better API test coverage would have caught before it reached production, since leadership responds better to a concrete cost-avoidance story than an abstract argument about test coverage percentages.
I would move from ad hoc, team-specific testing approaches toward a shared framework and baseline coverage expectations enforced through CI, since informal practices that worked with a handful of services break down once no single person has visibility into testing quality across hundreds of independently owned ones.
I try to ground the disagreement in concrete tradeoffs, like maintenance savings versus flexibility loss for teams with unusual requirements, and often land on making the shared framework the strong default while allowing a documented opt-out for genuinely justified cases, rather than forcing uniform adoption or leaving it entirely unstructured.
I would require framework design decisions to be documented with their rationale, beyond just usage instructions, and rotate ownership or pair newer testers with the framework maintainer on significant changes, so more people develop the deep familiarity needed to evolve it safely.
I present the specific risk of shipping with minimal coverage in terms of past incidents that inadequate testing has caused, letting leadership make an informed tradeoff rather than silently absorbing that risk or unilaterally blocking the deadline.
I would identify testers who already show strong judgment about framework design tradeoffs, beyond just proficiency writing individual tests, and give them ownership of smaller framework enhancements well before a transition, since that judgment about what belongs in a shared framework versus a team-specific extension is hard to build quickly.
I focus on making sure the underlying testing principles, like isolating test data and avoiding brittle exact-match assertions on dynamic values, transfer across paradigms, even though the specific tooling and request structure differ significantly between REST and GraphQL.
I would present a clear timeline of what happened, why existing test coverage didn't catch it, and the specific coverage gap being closed as a result, rather than a vague reassurance, since executives need enough concrete detail to assess whether similar gaps might exist elsewhere.
I would look at whether the suite's assertions are specific and meaningful, versus overly loose checks like only asserting a 200 status code without validating response content, since a large test count can mask genuinely shallow coverage that wouldn't catch a real regression.
I would quantify the flakiness and maintenance cost teams are currently absorbing from ad hoc, shared test fixtures that conflict under parallel execution, since that recurring cost, made visible, usually justifies the investment more clearly than an abstract reliability argument.
I try to separate what's genuinely unique about their auth flow from what's just unfamiliar to implement within the shared framework, since resistance sometimes comes from not yet seeing how the framework could extend to their case. Where the requirement is genuinely unusual, I'd rather support a documented extension point than force a bad fit.
I would push the organization toward pairing generated contract validation with Rest Assured-based behavioral testing rather than treating either as sufficient alone, since automatically generated contracts confirm structural shape but don't verify actual business logic correctness, which is where hands-on API tests still carry real, ongoing value.




