Cookies, storage and session reuse

Read and write cookies, work with local and session storage, and save an authenticated session so most tests skip the login form.

Cookies

# you must be on the domain before touching its cookies
driver.get("https://app.example.com")

print(driver.get_cookies())
print(driver.get_cookie("session_id"))

driver.add_cookie({
    "name": "feature_flags",
    "value": "new-checkout",
    "domain": "app.example.com",
    "path": "/",
})

driver.delete_cookie("feature_flags")
driver.delete_all_cookies()
  • Adding a cookie requires you to be on a page of that domain first; otherwise the browser rejects it silently.
  • A cookie written with a domain you do not control will not be stored, and Selenium does not raise an error.
  • delete_all_cookies() is the cheapest way to guarantee a test starts logged out.
  • Cookies set with HttpOnly are still visible to WebDriver, which is a good reason to keep session material out of test logs.

Local and session storage

driver.execute_script("return localStorage.getItem('theme');")
driver.execute_script("localStorage.setItem('theme', 'dark');")
driver.execute_script("return Object.keys(localStorage);")
driver.execute_script("localStorage.clear();")

# session storage is cleared when the tab closes
driver.execute_script("return sessionStorage.getItem('draftId');")

# grab everything as a dictionary
snapshot = driver.execute_script("""
    const out = {};
    for (let i = 0; i < localStorage.length; i++) {
        const k = localStorage.key(i);
        out[k] = localStorage.getItem(k);
    }
    return out;
""")
💡
Storage is per origin, so a snapshot taken on one host does nothing on another. When you restore a session, restore it on the same origin you captured it from, and verify with a request that would fail if you were logged out.

Saving and reusing a session

import json, pathlib, time
from selenium import webdriver

STATE = pathlib.Path("state/auth.json")

def login_and_save(driver, base_url):
    driver.get(base_url + "/login")
    driver.find_element(By.ID, "email").send_keys("qa@example.com")
    driver.find_element(By.ID, "password").send_keys("secret")
    driver.find_element(By.ID, "submit").click()
    WebDriverWait(driver, 10).until(
        lambda d: "/login" not in d.current_url
    )
    # give the app a moment to persist its token
    WebDriverWait(driver, 10).until(
        lambda d: "session_id" in {c["name"] for c in d.get_cookies()}
    )
    STATE.write_text(json.dumps({
        "cookies": driver.get_cookies(),
        "origin": base_url,
        "saved_at": int(time.time()),
    }))

def restore(driver, state):
    driver.get(state["origin"])
    for cookie in state["cookies"]:
        cookie.pop("sameSite", None)     # some drivers reject this key
        cookie.pop("expiry", None)
        driver.add_cookie(cookie)
    driver.refresh()
ApproachSpeedRobustness
Log in through the UI in every testSlowestHighest fidelity
Save cookies and reuseFastBreaks when the token format changes
Mint a token via the API and set itFastestNeeds an API and a known storage key
Persistent --user-data-dirFastLeaks state between runs
  1. Authenticate once in a session-scoped fixture and write the state to a JSON file.
  2. Reuse that file in every test that is not about authentication.
  3. Keep a small number of tests that do exercise the real login flow - they are your early warning when the mechanism changes.
  4. Store credentials in the environment, never in the state file or the repository.
  5. Treat the state file as a secret: it contains a live session token.

FAQ

Why does my saved session work locally but not in CI?
The saved cookies are bound to the origin they were captured on. In CI the base URL is usually different, so the cookie domain does not match and the browser discards them. Capture and restore against the same host, and parameterise the origin per environment.
Is reusing a session skipping the test?
No, as long as authentication is itself covered somewhere. Reusing a session removes a slow, repetitive step from tests that are about something else, and a handful of dedicated login tests keep the mechanism itself under test.

JavaScript execution and CDP Structuring a suite: page objects, pytest and parallelism

Last refreshed 2026-09-18.