skip to content

How would you choose between RestAssured.sessionId, SessionFilter and CookieFilter for a REST Assured suite?

level: principalimportance: should knowfreq 35%

answer

  1. choose by scope, not capability
  2. one static slot, one identity
  3. a filter instance is scopeable
  4. cookie store when it is not one value
  5. none of them re-authenticates

basics

~20 s

Pick the narrowest holder the suite actually needs. RestAssured.sessionId is one process-wide value, a SessionFilter instance is one identity you can scope, and a CookieFilter carries every cookie the server sets when the state is more than one id.

solid answer

~50 s

I decide on three axes: **scope**, **how many identities** the suite needs, and **how much of the logged-in state is a cookie**. `RestAssured.sessionId` is folded into the global `SessionConfig` when `given()` runs, so it is one value per process and needs `RestAssured.reset()` in teardown; it suits a suite where every call is the same keeper and nothing else. A `SessionFilter` instance is a live capture point you can scope as narrowly as a single test, so two identities means two instances rather than a global fight. A `CookieFilter` keeps a whole cookie store, exposed through `getCookieStore()`, and is what you need when `POST /apiary/session` sets a session cookie plus a hive-region cookie and both must return. My default is a scoped filter, because the static's biggest cost is that nothing in the test body says the session is there at all.

code

java · 58 lines
java
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.filter.cookie.CookieFilter;
import io.restassured.filter.session.SessionFilter;
import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification;
import org.junit.jupiter.api.Test;

import static io.restassured.RestAssured.given;

class HiveSessionHolderTest {

    private static final String BASE = "https://hives.example.test";

    @Test
    void twoKeepersNeedTwoHolders() {
        SessionFilter ada = signIn("ada");
        SessionFilter boris = signIn("boris");

        given().baseUri(BASE).filter(ada)
        .when().get("/apiary/hives")
        .then().statusCode(200).body("hiveId", org.hamcrest.Matchers.hasItem("h-42"));

        given().baseUri(BASE).filter(boris)
        .when().get("/hives/h-42/telemetry")
        .then().statusCode(403);
    }

    @Test
    void wholeCookieStoreWhenStateIsMoreThanOneId() {
        CookieFilter apiaryCookies = new CookieFilter();

        given().baseUri(BASE).filter(apiaryCookies)
            .contentType(ContentType.JSON)
            .body("{\"keeper\":\"ada\",\"passphrase\":\"combsecret\"}")
        .when().post("/apiary/session")
        .then().statusCode(200);

        given().baseUri(BASE).filter(apiaryCookies)
        .when().get("/hives/h-42/telemetry")
        .then().statusCode(200);
    }

    private SessionFilter signIn(String keeper) {
        SessionFilter filter = new SessionFilter();
        RequestSpecification spec = new RequestSpecBuilder()
                .setBaseUri(BASE)
                .setContentType(ContentType.JSON)
                .addFilter(filter)
                .build();

        given().spec(spec)
            .body("{\"keeper\":\"" + keeper + "\",\"passphrase\":\"combsecret\"}")
        .when().post("/apiary/session")
        .then().statusCode(200);

        return filter;
    }
}

go deeper

for a junior

Know that the three options exist and that a filter instance is safer than the global static, because a static session quietly applies to every test in the run.

for a middle

Explain how RestAssured.sessionId is folded into SessionConfig at given() time, and why that makes it process-wide until reset() runs.

for a senior

Choose deliberately for a real suite and justify it: one identity or several, one cookie or a store, and what teardown the choice obliges you to write.

for a principal

Own the scoping rule for the whole suite, including how a second identity or an expiring session would be absorbed without a rewrite, and what makes a wrong session visible in a failure.

## The three holders, and what each one actually is REST Assured gives you several places to keep a logged-in session between calls, and they differ in scope far more than in capability. - **`RestAssured.sessionId`** — a public static string. When `given()` runs, REST Assured folds a non-null value into the global configuration as `SessionConfig.sessionIdValue`, which the send step then applies as a cookie if no cookie of that name is present. Because the fold **rewrites `RestAssured.config`**, the effect outlives the test that set it until `RestAssured.reset()` runs. - **`RequestSpecBuilder.setSessionId(...)`** — the same value, held by a spec object instead of a static. Two roles means two specs, and each test says which one it used. - **`SessionFilter`** — one instance, one captured id, held in an internal reference. It captures on every response it sees and applies on every request that does not already name that cookie. - **`CookieFilter`** — one instance holding a cookie store. It replays each stored cookie the request URI matches, name-agnostically, and `getCookieStore()` exposes the store for inspection. ## Axis one: scope and lifetime This is the axis that decides most suites. A static is the widest possible holder: nothing in a test body mentions it, so a reader cannot tell from the test whether it is authenticated. It is also the one that leaks — a test that means to assert an anonymous `401` on `GET /hives/h-42/telemetry` gets a `200` instead, and the cause is three files away. The practical ranking, narrowest first: 1. A `SessionFilter` constructed inside the test that uses it. 2. A `SessionFilter` or spec held per fixture, for a group of tests that share one identity. 3. A spec built once per role and handed to tests by name. 4. `RestAssured.sessionId`, set globally. Prefer the narrowest the flow actually needs. If you do reach for the static, pair it with `RestAssured.reset()` in teardown as a rule, and know what else that undoes: `reset()` also clears the request and response specifications, the default parser, the proxy and the filter list, and replaces the config. ## Axis two: how many identities A suite that reads as one keeper is a different problem from one that checks that a keeper cannot see another apiary's hives. The moment two identities must be live at once, the static is out — there is one slot. Two `SessionFilter` instances, or two specs, express that directly: ```java SessionFilter adaSession = new SessionFilter(); SessionFilter borisSession = new SessionFilter(); ``` and a test that mixes them reads as what it is. ## Axis three: how much of the state is a cookie `SessionFilter` carries exactly one value, under the configured `SessionConfig.sessionIdName()`. If `POST /apiary/session` also sets a hive-region or tenant cookie that later reads depend on, the session filter will faithfully carry the session and quietly drop the rest. `CookieFilter` is the answer there: it stores whatever the response set and replays what matches. | holder | carries | scope | reach for it when | | --- | --- | --- | --- | | `RestAssured.sessionId` | one id, one name | whole process | every call is the same identity and nothing else matters | | spec `setSessionId(...)` | one id, one name | one spec object | roles are named and handed out explicitly | | `SessionFilter` | one captured id | one instance | sign-in and reads are separate and you never touch the value | | `CookieFilter` | a whole cookie store | one instance | logged-in state is several cookies, or names vary | ## The costs nobody mentions in the first meeting - **Invisibility.** The wider the holder, the less a failing test tells you about which session it ran under. A filter named in the chain is self-documenting; a static is not. - **Ordering coupling.** Because every mechanism except `given().sessionId(...)` stands down when the cookie is already set, a stray explicit cookie anywhere upstream silently disables the holder you chose. Wider holders make that harder to spot. - **Sharing.** Any of these becomes state shared between tests the moment its lifetime is wider than the tests that use it, which is a suite-architecture question rather than a REST Assured one — but it is REST Assured's scoping choices that decide how easy it is to get right. - **Refresh.** None of them re-authenticates. A `SessionFilter` keeps replaying a dead id happily; something outside the library has to notice and sign in again. ## How I would actually decide 1. If the suite has exactly one identity and short runs, a spec with `setSessionId(...)` built from one sign-in is the simplest honest answer. 2. If sign-in and the assertions are separate steps, or the id is never interesting to the test, use a `SessionFilter` scoped as narrowly as the identity allows. 3. If the API's logged-in state is more than a session id, use a `CookieFilter` and stop pretending it is one value. 4. Reach for `RestAssured.sessionId` only for a small, single-identity suite where the teardown discipline is already in place — and treat it as the thing you will remove first when the suite grows a second role. All four carry the same cookie in the end. What you are choosing is who owns it, for how long, and how obvious that is to the next person reading a red test.

  • What breaks first when a suite outgrows RestAssured.sessionId?
    The second identity. There is one static slot folded into one `SessionConfig`, so a test needing a different keeper has to overwrite it and put it back, and any test that forgets leaves the wrong session live. The symptom is usually a negative test that stops seeing its expected rejection.
  • How do you handle a session that expires part-way through a long run?
    Outside the library. Neither filter re-authenticates; a `SessionFilter` will replay a dead id indefinitely. You detect the rejection yourself and run the sign-in again, then either replace the filter instance or set the fresh id explicitly. REST Assured offers no refresh or retry hook for this.
  • Why is RestAssured.reset() a blunt instrument for undoing a global session id?
    Because it undoes far more. Alongside clearing the session id it drops the request and response specifications, the default parser, the proxy and the registered filters, and installs a fresh configuration. That is usually fine in a teardown, and surprising anywhere else.

saying these in an interview costs you the question

  • Defaulting to the global static because it needs the least code
  • Claiming a SessionFilter can carry two identities at once
  • Expecting any of these holders to re-authenticate on expiry
  • Using SessionFilter when the logged-in state is several cookies
  • Setting RestAssured.sessionId with no reset in teardown