skip to content

Which REST Assured filters carry state between requests, and where does that state actually live?

level: middleimportance: should knowfreq 51%

answer

  1. five forget, two remember
  2. state is on the object, not the spec
  3. cookie store versus one atomic reference
  4. a fresh instance per test carries nothing

basics

~20 s

Two of REST Assured's seven filters remember anything: CookieFilter and SessionFilter. Both keep their memory on the filter object, not on the request specification. So the same instance must be registered on every call, or each request starts empty.

solid answer

~50 s

`CookieFilter` and `SessionFilter` are the only two of the seven that carry anything forward, and both store it as **instance state**. `CookieFilter` holds an Apache `BasicCookieStore`: after each call it parses `Set-Cookie` from the response and, before each call, replays every stored cookie whose origin matches the request URI. `SessionFilter` holds a single `AtomicReference<String>` and carries only the session identifier. Because the memory is on the object, a `new CookieFilter()` created inside each test method carries nothing -- you must hold one instance as a field and register that. Two smaller rules matter: by default `CookieFilter` will not overwrite a cookie you set explicitly on the request, and sharing one instance across parallel workers makes it shared mutable state like any other. Scope that instance deliberately, since it outlives every single test that registers it.

code

java · 40 lines
java
import io.restassured.filter.cookie.CookieFilter;

import static io.restassured.RestAssured.given;

public class MuseumSessionCarry {

    // ONE instance held as a field: this is what makes the carry work
    private final CookieFilter cookies = new CookieFilter();

    public void loginThenBuyThenCheckIn() {
        given()
                .baseUri("https://api.museum.example")
                .filter(cookies)
                .formParam("email", "[email protected]")
                .formParam("password", "s3cret")
                .post("/v1/sessions")
        .then()
                .statusCode(200);

        // the login cookie is replayed automatically by the same filter
        String ticketCode = given()
                .baseUri("https://api.museum.example")
                .filter(cookies)
                .contentType("application/json")
                .body("{\"exhibitionId\":\"pompeii\",\"slotId\":\"2026-04-11T10:00\"}")
                .post("/v1/tickets")
        .then()
                .statusCode(201)
                .extract().path("ticketCode");

        // an explicitly set cookie of the same name is NOT overwritten
        given()
                .baseUri("https://api.museum.example")
                .filter(cookies)
                .cookie("visitorPrefs", "large-print")
                .post("/v1/visits/{code}/check-in", ticketCode)
        .then()
                .statusCode(200);
    }
}

go deeper

for a junior

Remember the pair: CookieFilter and SessionFilter are the two that carry something forward. Register the same instance on each call rather than constructing a new one inside the test.

for a middle

Explain the storage. CookieFilter holds a cookie store and replays cookies whose origin matches; SessionFilter holds a single atomic reference and carries only the session identifier. Both are instance fields, which is why reuse matters.

for a senior

Show that you handle the lifecycle: where the instance is held, how it is reset between suites, and what happens when several workers share it. Also know that an explicitly set cookie beats the filter's stored copy by default.

for a principal

Own the harness-state policy. A stateful filter is convenient and it is shared mutable state, so decide up front whether the suite's isolation model gives each worker its own instance or forbids the pattern outright.

Of REST Assured's seven shipped filters, five are pure functions of one call: the four logging filters print and forget, and `TimingFilter` measures and forgets. Exactly two remember something from one request to the next, and the interesting part of the question is not *which two* but *where the memory sits*. ## The two, and their storage | Filter | What it remembers | Where it lives | | --- | --- | --- | | `CookieFilter` | Every cookie the server set, matched by origin | A `BasicCookieStore` field on the instance | | `SessionFilter` | The session identifier only | An `AtomicReference<String>` field on the instance | Neither stores anything on the request specification, on a thread local, or in a static. **The filter object is the storage.** That single fact explains almost every problem people hit with these two. ## How CookieFilter behaves on each call Its `filter(...)` method does three things in order: 1. It builds a cookie origin from `requestSpec.getURI()` -- host, port, path and whether the scheme is HTTPS. 2. For every cookie already in the store, it checks the origin match and adds the cookie to the request. 3. After `ctx.next(...)` returns, it parses each `Set-Cookie` header from the response and adds the results to the store. Two details in step 2 are worth naming. The origin match uses Apache's strict RFC 6265 specification, so a cookie scoped to a different host or path is held but not replayed. And by default the filter **will not** add a stored cookie whose name is already present on the request -- if a test sets `given().cookie("visitorPrefs", "large-print")` explicitly, that explicit value wins. The one-argument constructor `new CookieFilter(true)` changes that: it allows multiple cookies with the same name, which is what you want when a server legitimately scopes two same-named cookies to different paths. There is also a redirect subtlety. When the server answers with a 302, Apache HttpClient follows it internally and REST Assured's request URI still points at the original address, so the filter reads the effective URI back out of the client's context before deciding which cookies the response is allowed to set. ## How SessionFilter behaves `SessionFilter` is deliberately narrower. Before the call, if it has a stored identifier and the request does not already carry a session cookie, it sets one. After the call, it reads the session identifier from the response and stores it if it is not blank. It does not touch any other cookie. When a suite needs the whole cookie jar rather than one identifier -- a locale cookie, a consent cookie, a CSRF cookie alongside the session -- `CookieFilter` is the one you want. ## The mistake this question is really testing The single most common failure is a fresh instance per test: - `given().filter(new CookieFilter())` inside a test method creates an **empty** store every time, so nothing is ever carried anywhere. The code looks right and does nothing. - Registering the filter on a `RequestSpecBuilder` that is itself rebuilt per test has the same effect, one level up. - The fix is to hold the filter as a field or a constant and register that same object: one instance, many calls. The mirror-image mistake is over-sharing. Because the state is plain instance state, one `CookieFilter` shared between test workers running at the same time is shared mutable state, with all the usual consequences -- one worker's login cookie replayed onto another worker's request. That is a harness-architecture problem rather than a REST Assured one, and the answer is the ordinary one: give each worker its own instance. ## Choosing between them, on a museum ticketing suite A suite that authenticates through `POST /v1/sessions` and then calls `POST /v1/tickets` and `POST /v1/visits/{ticketCode}/check-in` has a clear decision: - If the login response sets only a session cookie, `SessionFilter` is the smaller, more explicit choice. - If the login response also sets a consent or locale cookie that later endpoints read, `CookieFilter` is the one that carries all of them. - If some endpoints are scoped under different paths and the server sets same-named cookies for each, `new CookieFilter(true)` is the constructor you need. - If a specific test must send a deliberately stale or wrong cookie, set it explicitly with `given().cookie(...)` and the default `CookieFilter` will step aside rather than overwrite it. The answer an interviewer wants in one line: two of the seven carry state, both keep it on the instance, and reusing that instance is the whole technique.

  • What happens if a test sets a cookie explicitly that CookieFilter also has stored?
    By default the explicit value wins: the filter checks whether the request already carries a cookie with that name and skips its own stored copy. Constructing it as `new CookieFilter(true)` allows multiple cookies with the same name, so both are sent -- which is the right choice when a server scopes same-named cookies to different paths.
  • Is one shared CookieFilter safe when the suite runs tests in parallel?
    No more than any other shared mutable object. The cookie store is ordinary instance state, so two workers registering the same filter can replay each other's cookies. Give each worker its own instance. This is the general thread-safe-harness-state problem rather than anything specific to REST Assured.

saying these in an interview costs you the question

  • Constructing a new CookieFilter in every test and expecting persistence
  • Believing the cookie store lives on the request specification
  • Thinking SessionFilter carries every cookie the server sets
  • Assuming CookieFilter overwrites a cookie you set explicitly
  • Sharing one stateful filter across parallel workers without thought