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
HttpOnlyare 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("[email protected]")
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()| Approach | Speed | Robustness |
|---|---|---|
| Log in through the UI in every test | Slowest | Highest fidelity |
| Save cookies and reuse | Fast | Breaks when the token format changes |
| Mint a token via the API and set it | Fastest | Needs an API and a known storage key |
Persistent --user-data-dir | Fast | Leaks state between runs |
- Authenticate once in a session-scoped fixture and write the state to a JSON file.
- Reuse that file in every test that is not about authentication.
- Keep a small number of tests that do exercise the real login flow - they are your early warning when the mechanism changes.
- Store credentials in the environment, never in the state file or the repository.
- 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.
Related
JavaScript execution and CDP Structuring a suite: page objects, pytest and parallelism
Last refreshed 2026-09-18.