Authenticated browser journeys are where Test automation stops being a clean page-object demo and starts looking like real production behavior. A user logs in, gets bounced through an identity provider, completes MFA, lands back in the app, opens a second tab, refreshes, closes the browser, reopens the next day, and sometimes still remains signed in. If your tests cannot model those transitions, you do not really know whether your app handles real sessions correctly.

That is why Playwright vs Selenium session persistence matters as a practical comparison, not just a feature checklist. Both tools can drive login flows, but they differ in how easily they let you preserve cookies, local storage, and authenticated state across redirects, reloads, tabs, and fresh browser contexts. Those differences show up in test reliability, setup cost, and how much custom plumbing your team has to maintain.

Session persistence testing is not only about “staying logged in.” It is about validating whether your app, identity provider, browser state, and test harness agree on what authenticated means after redirects, tab changes, and expiry.

What session persistence actually means in browser tests

Before comparing tools, it helps to define the problem precisely. In browser automation, session persistence usually includes several different behaviors:

  • Login redirect testing, verifying that an unauthenticated user is redirected to the identity provider, then returns to the intended page after login.
  • MFA flows, including TOTP, push approval, WebAuthn, SMS, or backup codes.
  • Authenticated browser sessions, where cookies, session storage, local storage, or client-side tokens survive reloads and page transitions.
  • Tab recovery, where a user closes a tab or opens a new tab and the app should recognize an existing session.
  • Expiry handling, where the session eventually expires and the app must reauthenticate or redirect cleanly.

These scenarios can involve both browser state and server state. A cookie might still exist while the server session has expired. A JWT might still be stored in local storage while the backend refuses it. A tab might reopen with stale client-side state that no longer matches the session cookie. Good tests need to observe those seams.

For the underlying browser automation concepts, it is worth skimming the official docs for Playwright and Selenium.

The core difference in how Playwright and Selenium model sessions

The biggest practical difference is not “can they do it,” because both can. It is how much native support they provide for isolating, saving, and reusing browser state.

Playwright: context-first session control

Playwright is built around the idea of browser contexts, which are isolated, lightweight browser sessions. A context can own its own cookies, local storage, session storage, permissions, and tabs. That makes session persistence workflows fairly direct:

  • start a clean context for the login flow,
  • authenticate once,
  • save the authenticated state to disk,
  • reuse that state in later tests.

This is a strong fit when you want deterministic state management. It also maps cleanly to test suites that separate authentication setup from functional assertions.

Selenium: browser-instance control with manual state management

Selenium works through browser drivers and sessions. You can absolutely persist authentication, but the tool is more low-level about it. Common approaches include:

  • reusing a browser profile,
  • exporting and re-adding cookies,
  • injecting local storage values via JavaScript,
  • keeping a WebDriver session alive across test steps.

That is flexible, but the burden is on your framework design. If your app relies heavily on local storage, cross-tab behavior, or conditional redirects, you often end up writing helper code to reconstruct state accurately.

Login redirect testing, where the two tools diverge in practice

Login redirect testing sounds simple until you automate a modern auth flow. A user hits /billing, the app redirects to the IdP, the IdP sends them back with a code or token, and the app then finalizes the session. If the redirect chain is not stable, you can get intermittent failures that are hard to diagnose.

Playwright approach

In Playwright, a common pattern is to capture the authenticated state once and reuse it. That avoids logging in through the full redirect chain on every test.

import { test as setup, expect } from '@playwright/test';

setup(‘authenticate’, async ({ page }) => { await page.goto(‘https://app.example.com/login’); await page.getByLabel(‘Email’).fill(process.env.TEST_USER_EMAIL!); await page.getByLabel(‘Password’).fill(process.env.TEST_USER_PASSWORD!); await page.getByRole(‘button’, { name: ‘Sign in’ }).click(); await expect(page).toHaveURL(/dashboard/); await page.context().storageState({ path: ‘storage-state.json’ }); });

Then later tests load the saved state and skip the full login path:

import { test, expect } from '@playwright/test';

test.use({ storageState: ‘storage-state.json’ });

test('opens billing while authenticated', async ({ page }) => {
  await page.goto('https://app.example.com/billing');
  await expect(page.getByRole('heading', { name: 'Billing' })).toBeVisible();
});

This is especially useful when the app redirects to multiple identity steps or when SSO provider latency makes the raw login path flaky.

Selenium approach

With Selenium, you can test the redirect chain directly, but persisting the resulting state usually takes more work. A Python example might look like this:

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome() driver.get(‘https://app.example.com/login’) driver.find_element(By.ID, ‘email’).send_keys(‘user@example.com’) driver.find_element(By.ID, ‘password’).send_keys(‘secret’) driver.find_element(By.CSS_SELECTOR, ‘button[type=”submit”]’).click()

cookies = driver.get_cookies() print(cookies)

To reuse the session later, you typically need to open the same domain first, add cookies back, and sometimes restore storage values with JavaScript. That works, but it is more framework-dependent and easier to get wrong if the app uses short-lived tokens or path-scoped cookies.

If login redirect testing is a primary use case, Playwright usually gives you a cleaner state model. Selenium can do it, but you are closer to the metal, which means more glue code and more edge cases to maintain.

MFA flows are the real stress test

MFA is where teams discover whether their automation strategy is realistic. A flow might require a TOTP code, a push approval, a hardware key, or a one-time backup code. The test question is not just whether the UI can submit MFA. It is whether the post-MFA state remains valid across redirects and new tabs.

What to validate in MFA tests

For MFA-related session persistence, useful checks include:

  • the app returns to the correct destination after MFA,
  • the MFA step does not drop intended navigation state,
  • refreshing after MFA preserves the authenticated session,
  • a new tab opened after MFA inherits the expected session state,
  • invalid or expired MFA attempts do not partially authenticate the session.

Playwright strengths here

Playwright is often easier for MFA workflows because you can keep the test in a single isolated context and observe each page transition. That makes it straightforward to:

  • wait for redirects without hard sleeps,
  • assert URL changes precisely,
  • capture state after MFA completes,
  • reuse that state in other tests.

If the MFA provider opens a popup or a separate page, Playwright’s page and context APIs tend to keep the flow manageable.

Selenium strengths here

Selenium is perfectly capable of driving MFA, especially when the process is mostly form-based. The challenge is usually not the interaction itself, but the session handoff after MFA. If your app stores part of the auth result in cookies and part in browser storage, you may need extra helper methods to preserve the full authenticated state across test steps or across browser restarts.

For teams already invested in Selenium, this often becomes an argument for building a dedicated authentication helper layer rather than scattering login logic throughout tests.

Tab recovery and multi-tab behavior

Tab recovery is where browser automation can drift away from user reality. End users open links in new tabs, lose tabs, refresh pages, or return to a session after a browser crash. If your app relies on client-side auth state, those scenarios can behave differently from a simple page reload.

Playwright handles isolated tabs more naturally

Because Playwright models multiple pages inside a browser context, it is relatively easy to open a new tab and inspect whether the session remains intact.

import { test, expect } from '@playwright/test';

test.use({ storageState: ‘storage-state.json’ });

test('session survives a new tab', async ({ context }) => {
  const page = await context.newPage();
  await page.goto('https://app.example.com/account');
  await expect(page.getByText('Account settings')).toBeVisible();
});

If your application depends on a shared cookie jar or shared local storage, Playwright’s context-level structure makes the relationship explicit.

Selenium can mimic recovery, but it is less elegant

Selenium can open multiple windows or tabs through WebDriver APIs, but session reuse across them can be less transparent, especially when the browser behavior depends on profile settings or custom startup arguments. For recovery scenarios, teams often end up testing one of three things:

  • browser session survives a refresh,
  • browser profile survives a process restart,
  • app state reconstructs itself from a valid cookie or token.

That last item is especially important. A tab recovery test should not assume that a page refresh is equivalent to a real restart. If your app stores the entire auth state in memory, the recovery story is probably weak no matter which tool you use.

A common mistake is to treat persistence as a binary problem, either the user is logged in or not. In production, session validity is more nuanced.

Consider these cases:

  • the cookie is present, but the server has rotated or revoked the session,
  • the access token is valid, but the refresh token is not,
  • the UI shows an authenticated view, but the API starts returning 401s,
  • the session survives reloads, but not browser restart,
  • the app silently renews sessions in one browser and fails in another.

Playwright’s saved storageState is excellent for repeatable tests, but it can also hide overly optimistic assumptions if you never test expiry behavior. Selenium’s more manual approach can be useful here, because you may be forced to test the actual browser behavior and not just a pre-baked state snapshot.

A healthy suite usually needs both:

  1. fast tests that reuse known-good authenticated state,
  2. a smaller number of end-to-end tests that re-run the entire login, MFA, and redirect path.

Practical decision criteria for QA and SDET teams

If you are deciding between these tools for session-heavy tests, these are the questions that matter.

Choose Playwright first if you need

  • reliable login redirect testing with less harness code,
  • easy saving and reusing of authenticated browser sessions,
  • stable tab and multi-page scenarios inside one browser context,
  • fast setup for isolated auth state in parallel test runs,
  • a framework that makes storage state a first-class concept.

Choose Selenium first if you need

  • broad language and ecosystem flexibility across existing stacks,
  • compatibility with a large legacy suite already built around WebDriver,
  • custom control over browser profiles, drivers, and session internals,
  • deep integration with existing grid or enterprise infrastructure.

Use both patterns when needed

For many teams, the answer is not “replace everything.” A more realistic pattern is:

  • keep Selenium where the suite is large and stable,
  • use Playwright for new session-heavy coverage,
  • migrate the flakiest login and MFA paths first,
  • keep a small number of true end-to-end authentication tests in the main CI pipeline.

If you are evaluating broader platform tradeoffs, a Playwright alternative with managed workflows can be relevant for teams that want agentic AI-assisted test creation and less framework maintenance, especially when non-developers need to participate in authentication flow coverage. For teams planning a migration, Endtest also publishes Selenium migration guidance that can help map old tests into a newer workflow.

Implementation patterns that reduce flakiness

No matter which tool you use, the same engineering habits improve session persistence tests.

1. Separate auth setup from feature assertions

Do not log in through the UI in every test unless you are explicitly testing the login path. Save the authenticated state once and reuse it.

2. Prefer state assertions over arbitrary waits

Avoid waiting for a fixed delay after redirect or MFA approval. Assert on URL, cookies, visible user identity, or API-backed UI state.

3. Verify both client and server expectations

A UI that says “Welcome back” is not enough. Confirm that the backing API accepts the session and that protected routes behave consistently.

4. Test profile restart, not just page reload

A session that survives reload() but fails after the browser restarts is not truly persistent. Include restart tests for critical auth journeys.

5. Watch for cross-origin storage assumptions

If your app uses a third-party IdP, the auth sequence may span origins. Some state lives in first-party cookies, some in storage, some in server-side session records. Your test needs to reflect that complexity.

Where Cypress fits, and why it is not the main comparison here

Cypress can test authenticated flows too, but for session persistence across redirects and browser recovery, it often ends up compared differently because of its browser model and command queue. If your team is already looking at broader options, the right decision usually depends on whether you need cross-browser depth, session serialization, or the most direct auth-state control. That is why Playwright and Selenium are still the central comparison for many SDET teams.

A simple rule of thumb

If your most painful problem is “we keep losing auth state between tests,” Playwright usually gives you the smoother path forward. If your most painful problem is “we already have a huge Selenium investment and need to preserve it,” Selenium remains viable, but you will likely need a more deliberate session helper layer.

If your organization wants to reduce framework ownership altogether, an optional platform like Endtest can be worth evaluating, especially because it is positioned as an agentic AI test automation platform with low-code workflows and real-browser execution. That matters most when the bottleneck is not just session persistence, but maintaining a larger end-to-end suite over time.

Conclusion

For browser session persistence, the real question is not whether Playwright or Selenium can click through a login form. It is which tool gives your team the most reliable control over authenticated state across redirects, MFA checkpoints, browser tabs, refreshes, restarts, and expiration.

Playwright is usually the better fit when you want a cleaner, context-based model for saving and reusing authenticated sessions. Selenium remains a strong option when legacy coverage, language flexibility, or existing infrastructure is the priority. Both can validate login redirect testing, MFA flows, tab recovery, and authenticated browser sessions, but Playwright typically reduces the amount of custom plumbing needed to get there.

If session persistence is a major source of test flakiness, the best next step is not a full rewrite. Start by isolating authentication setup, saving session state explicitly, and adding a small set of restart and expiry checks. That gives you a much clearer picture of whether your app’s auth story is truly durable, or just passing because the browser never stopped moving.