Isolating browser sessions in Playwright tests means giving each test its own clean browser context, so cookies, localStorage, and login states never leak between tests. This guide shows you how to structure your tests for reliable, parallel, and repeatable runs using Playwright’s built-in fixtures and storage state features.
What You’ll Need
- Node.js (v16 or later) installed
- A project with Playwright installed (
npm init playwright@latest) - Basic familiarity with writing tests in JavaScript or TypeScript
Step-by-Step
- Understand the default isolation
Playwright already creates a new browser context for each test by default. This means cookies, localStorage, and sessions are cleared between tests automatically. If you’re using the defaulttestfunction, you’re already isolated. The challenge is when you want to share a logged-in state across tests or when you need to control the context manually. - Use the default test fixture
Write your tests using thetestfunction from@playwright/test. Each test receives apagefixture that is tied to a fresh context. Avoid creating your ownbrowser.newContext()unless you specifically need multiple contexts in one test. - Set up a login helper
Create a helper function that logs in to your app and returns the storage state. For example, in ahelpers.tsfile:import { Browser } from 'playwright'; export async function loginAndGetState(browser: Browser) { const context = await browser.newContext(); const page = await context.newPage(); await page.goto('https://example.com/login'); await page.fill('#username', 'testuser'); await page.fill('#password', 'password123'); await page.click('button[type=submit]'); await page.waitForURL('https://example.com/dashboard'); const state = await context.storageState(); await context.close(); return state; } - Reuse the state in tests
In your test file, use Playwright’sstorageStateoption when creating a context. The simplest way is to use thetest.use()method to set the storage state for a group of tests:test.use({ storageState: 'storageState.json' });To generate the
storageState.jsonfile, run a script that logs in once and saves the state usingcontext.storageState({ path: 'storageState.json' }). - Use project dependencies (optional but recommended)
In yourplaywright.config.ts, define a setup project that logs in and saves the storage state, then make your test project depend on it. This runs the login once before all tests and reuses the state across workers.projects: [ { name: 'setup', testMatch: /setup\.ts/ }, { name: 'chromium', use: { ...devices['Desktop Chrome'] }, dependencies: ['setup'] }, ] - Isolate tests that need unique sessions
If a test must start with a blank session (e.g., testing logout or registration), you can override the storage state for that specific test usingtest.use({ storageState: { cookies: [], origins: [] } })or by creating a new context manually withbrowser.newContext()inside the test. - Run tests in parallel
Because each test has its own context, you can safely run tests in parallel. Playwright’s default is to run tests in a single file in order, but you can enable parallelism across files by settingworkersin the config. The isolation ensures no cross-test interference. - Clean up after tests
Playwright automatically closes contexts and pages at the end of each test. If you create contexts manually, always close them in afinallyblock or usetest.afterEach()to avoid resource leaks.
Common Mistakes
- Sharing a single context across tests – If you create a context in a
beforeAllhook and reuse it, tests will share cookies and state, breaking isolation. - Forgetting to save storage state – If you generate a storage state file but don’t update it after app changes, tests will fail with stale sessions.
- Overriding storage state globally when not needed – Applying a logged-in state to every test can mask issues with login flows and make tests less realistic.
- Not closing manual contexts – Leaving contexts open can cause memory leaks and slow down test runs.
Tips
- Use a dedicated setup script to generate storage state, and commit the resulting JSON file to version control if it contains no sensitive data.
- For multiple user roles (admin, regular user), generate separate storage state files and use them in different test groups.
- Use environment variables for credentials in your setup script to avoid hardcoding passwords.
- Run tests with
--workers=4to see how isolation enables parallel execution. - If your app uses WebSockets or IndexedDB, test that those are also isolated per context – they are, but be aware.
Troubleshooting
- Tests pass individually but fail in parallel – This usually indicates shared state. Check if you’re using a global variable or a file that persists data. Ensure each test uses its own context.
- Storage state file is missing or empty – Run the setup script manually. Ensure the script waits for the login to complete before saving the state.
- Login state expires mid-run – If your app has short session timeouts, consider regenerating the storage state more frequently, or use a longer-lived token in the setup.
- Context is not isolated – Verify you’re using the
pagefixture, not a manually created context. If you must create a context, do it inside the test, not in a shared hook. - Parallel tests are slow – This is often due to resource contention. Increase the number of workers gradually, and make sure your app can handle concurrent sessions.
Frequently Asked Questions
Does Playwright isolate sessions by default?
Yes, each test gets a fresh browser context by default, which includes a clean session. You don’t need to do anything extra for basic isolation.
How do I share a login state across tests without breaking isolation?
Use Playwright’s storage state feature. Log in once in a setup script, save the state to a JSON file, and then use test.use({ storageState: 'file.json' }) in your test groups. Each test still gets its own context, but it starts with that saved state.
Can I run tests in parallel with isolated sessions?
Absolutely. In fact, isolation is what makes parallel execution safe. Each test runs in its own context, so there’s no interference.
What if I need two different sessions in the same test?
You can create additional contexts manually inside the test using browser.newContext() and close them at the end. This is useful for testing chat features or multi-user flows.
Should I use test.describe.serial if I need shared state?
No, that’s for controlling execution order, not for sharing state. For shared state, use storage state or a fixture that runs before the tests. Avoid sharing contexts directly.
How do I clear storage state for a specific test?
Override the storage state for that test with test.use({ storageState: { cookies: [], origins: [] } }) or create a fresh context manually. This ensures the test starts with no session.
Related
General steps for current mainstream versions; menus can differ by device and version. See the disclaimer or report an error.
More in this bed
- How to Make Group Chats on iPhone
- Spreadsheet vs Database: Key Differences
- How to Set a Password on a 7-Zip Archive
- Bond Market Volatility: How It Hits Your Funds
- How to Watch Live TV on a Smart TV: Antenna or Apps
- Fix Windows Error 0x800745d & Check 0x8007045d