Resource library

QA How-To

Cypress cy.session: Examples

Use each cypress cy.session example for UI login, API cookies, localStorage tokens, roles, tenants, validation, cross-spec caching, SSO, and debugging.

20 min read | 2,362 words

TL;DR

A production-ready cy.session example wraps one complete login flow in a shared helper, uses a deterministic non-secret ID, validates a protected identity endpoint, and visits the test route afterward. Pick the recipe below that matches cookie, token, role, tenant, cross-spec, or SSO behavior.

Key Takeaways

  • Choose a UI, cookie API, or token setup that recreates the authentication state your application truly consumes.
  • Use an array or object ID that distinguishes identity, role, tenant, and login strategy without exposing credentials.
  • Validate the exact user and authorization context through a protected, idempotent endpoint.
  • Call cy.visit after the login helper so the test owns its destination and works with test isolation.
  • Use cacheAcrossSpecs for one run and machine only, with a single shared session definition.
  • Keep expired-session and logout scenarios separate from the healthy reusable login cache.
  • Diagnose creation, restoration, and validation separately before adding waits or clearing caches.

The best cypress cy.session example depends on where your application stores authentication. Cookie-based apps can log in through cy.request(), token-based apps may initialize localStorage, and browser-only identity flows can run the login UI inside the setup callback. Every version needs a safe cache ID and meaningful validation.

This guide is a recipe collection rather than a command reference. Each pattern explains when to use it, what it proves, and what to change before committing it. The routes and credentials are illustrative application contracts, while every Cypress API shown is real and current for 2026.

TL;DR

Application model Setup recipe Recommended validation
Server session cookie Login through cy.request() GET /api/me returns exact user
Token in local storage Request token, set expected key Protected endpoint returns exact scopes
UI-only login Visit login and submit form Identity endpoint or protected page
Multiple roles Include role in ID Assert role and required permission
Multiple tenants Include tenant in ID Assert tenant boundary
Cross-spec reuse Add cacheAcrossSpecs: true Same shared helper in every spec

The minimum reliable shape is:

cy.session(id, setup, {
  validate() {
    cy.request('/api/me').its('status').should('eq', 200)
  },
})

1. Cypress cy.session Example With UI Login

Use UI login setup when no approved programmatic path exists or when the identity screen itself establishes essential browser state. The setup callback must prove login has completed before Cypress captures the session. An assertion on a post-login URL and stable account element is stronger than clicking Submit and ending immediately.

function loginThroughUi(email: string, password: string) {
  return cy.session(
    ['ui-login', email],
    () => {
      cy.visit('/login')
      cy.get('[data-testid=email]').type(email)
      cy.get('[data-testid=password]').type(password, { log: false })
      cy.contains('button', 'Sign in').click()

      cy.location('pathname').should('eq', '/dashboard')
      cy.get('[data-testid=user-menu]').should('be.visible')
    },
    {
      validate() {
        cy.request('/api/me').its('status').should('eq', 200)
      },
    },
  )
}

it('opens a project after UI login', () => {
  loginThroughUi('member@example.test', 'secret-from-config')
  cy.visit('/projects/prj_1001')
  cy.contains('h1', 'Release plan').should('be.visible')
})

Keep the password outside source control and do not include it in the ID. The { log: false } typing option reduces exposure in the Command Log, but recordings and application logs need their own protection.

This cached setup does not replace dedicated tests for the login screen. It makes login a precondition for unrelated project tests. Keep a small focused authentication spec that still verifies form validation, password errors, accessibility, redirects, and recovery behavior without relying on the cached helper.

2. API Cookie Login Example

A direct login request is usually the fastest recipe for a server-managed session cookie. Cypress applies response cookies to the browser cookie store, so a successful authentication call can prepare the next page visit. Validate both the exact identity and expected role.

type CurrentUser = {
  id: string
  email: string
  role: 'member' | 'admin'
}

function loginByCookie(email: string, role: CurrentUser['role']) {
  return cy.session(
    ['cookie-login', email, role],
    () => {
      cy.request({
        method: 'POST',
        url: '/api/auth/session',
        body: { email, role, password: 'secret-from-config' },
        log: false,
      }).then((response) => {
        expect(response.status).to.eq(204)
        cy.getCookie('session_id').should('exist')
      })
    },
    {
      validate() {
        cy.request<CurrentUser>('/api/me').then(({ status, body }) => {
          expect(status).to.eq(200)
          expect(body).to.include({ email, role })
        })
      },
    },
  )
}

A cookie-existence check belongs in setup as a useful diagnostic, but it is not enough for validation. Cookies can exist after expiration or point to a different user. The protected identity request proves the server accepts the state and returns the expected context.

Call the helper before each test that needs the identity. The first call creates the session, while later calls restore and validate it. For details on the underlying request behavior, see Cypress cy.request for API testing.

3. Token and localStorage Example

Some single-page applications receive a bearer token and read it from localStorage during startup. Request the token, visit the application origin, and set the exact key before the app initializes. The onBeforeLoad callback makes that order explicit.

type TokenPayload = {
  accessToken: string
  expiresIn: number
}

function loginByToken(email: string) {
  return cy.session(
    ['token-login', email],
    () => {
      cy.request<TokenPayload>({
        method: 'POST',
        url: '/api/auth/token',
        body: { email, password: 'secret-from-config' },
        log: false,
      }).then(({ status, body }) => {
        expect(status).to.eq(200)
        expect(body.expiresIn).to.be.greaterThan(0)

        cy.visit('/', {
          onBeforeLoad(win) {
            win.localStorage.setItem('access_token', body.accessToken)
          },
        })
      })
    },
    {
      validate() {
        cy.request('/api/me').its('status').should('eq', 200)
      },
    },
  )
}

This is appropriate only when the real application uses the same storage key and token format. Do not create a test-only state that bypasses initialization behavior. If the app expects a JSON object with expiry metadata, store that documented object instead of just the raw token.

Avoid parsing or modifying a signed token in the test unless token content itself is the feature under test. Treat it as an opaque credential and validate behavior through the service. Tokens must stay out of aliases, session IDs, screenshots, thrown error messages, and custom logs.

4. Multiple Users, Roles, and Tenants

A single ID such as user-session is unsafe when scenarios switch identities. Use a structured ID that describes the resulting state. This example covers two roles in one spec without restoring the administrator state for the member.

type LoginContext = {
  email: string
  role: 'member' | 'admin'
  tenantId: string
}

function loginAs(context: LoginContext) {
  const { email, role, tenantId } = context

  return cy.session(['login', email, role, tenantId], () => {
    cy.request({
      method: 'POST',
      url: '/test-support/session',
      body: context,
    }).its('status').should('eq', 204)
  }, {
    validate() {
      cy.request<LoginContext>('/api/me')
        .its('body')
        .should('include', context)
    },
  })
}

describe('workspace permissions', () => {
  it('allows an admin to open workspace settings', () => {
    loginAs({
      email: 'admin@example.test',
      role: 'admin',
      tenantId: 'tenant-a',
    })
    cy.visit('/settings/workspace')
    cy.contains('h1', 'Workspace settings').should('be.visible')
  })

  it('denies a member access to workspace settings', () => {
    loginAs({
      email: 'member@example.test',
      role: 'member',
      tenantId: 'tenant-a',
    })
    cy.visit('/settings/workspace')
    cy.contains('[role=alert]', 'You do not have permission').should('be.visible')
  })
})

A test-support session endpoint must be protected from production and reviewed with security. It should create the same cookie and server-side authorization model as normal login, not a magical client flag that bypasses enforcement.

If a test changes a user's role on the server, the cached snapshot is not updated. Use a separate identity or state key, and validate role on every restore. This is especially important in parallel runs where one shared user's permissions can be mutated by another spec.

5. Cypress cy.session Example Validation Patterns

Validation should be fast, idempotent, and specific to the intended state. Choose the narrowest check that detects expired, revoked, incomplete, or wrong-identity sessions.

Validation Strength Tradeoff
Cookie exists Weak Fast, but accepts expired or wrong-user cookies
Protected endpoint is 200 Good Proves auth, but not exact role or tenant
Identity body matches context Strong Slightly more assertion code
Protected page renders Useful fallback Slower and coupled to UI
Destructive write succeeds Poor Adds side effects on every restore

Here is a validation block that gives a clear failure for 401, 403, or an identity collision.

validate() {
  cy.request<{ email: string; tenantId: string; permissions: string[] }>({
    url: '/api/me',
    failOnStatusCode: false,
  }).then(({ status, body }) => {
    expect(status, 'session should be authorized').to.eq(200)
    expect(body.email).to.eq(email)
    expect(body.tenantId).to.eq(tenantId)
    expect(body.permissions).to.include('project:read')
  })
}

Validation runs after initial setup and after restoration. If it fails immediately after setup, the test fails. If it fails for restored state, Cypress reruns setup and validates the new state. Do not add an unconditional cy.wait() to give an invalid session more time. Fix setup completion or validate a real readiness condition.

For a broader design discussion, read how to use Cypress cy.session.

6. cacheAcrossSpecs Example for CI

Use cacheAcrossSpecs: true when several specs on the same machine share one stable state. Put the complete definition in support code so every spec invokes identical setup, validation, and options.

// cypress/support/auth.ts
export function loginShared(email: string) {
  return cy.session(['shared-cookie-login', email], () => {
    cy.request({
      method: 'POST',
      url: '/api/auth/session',
      body: { email, password: 'secret-from-config' },
      log: false,
    }).its('status').should('eq', 204)
  }, {
    validate() {
      cy.request<{ email: string }>('/api/me')
        .its('body.email')
        .should('eq', email)
    },
    cacheAcrossSpecs: true,
  })
}
// cypress/e2e/projects.cy.ts
import { loginShared } from '../support/auth'

beforeEach(() => {
  loginShared('stable-member@example.test')
  cy.visit('/projects')
})

The cache exists for one Cypress run on one machine. A new run starts empty. If CI distributes specs over four machines, each machine performs setup for its own cache. Plan identity-provider quotas and test users accordingly.

Cross-spec caching is risky when specs mutate server-side account state. A restored cookie does not restore a deleted cart, changed locale, revoked permission, or modified profile. Use a stable read-only identity, isolate a tenant per worker, or reset backend state explicitly. The Cypress parallel testing guide covers worker ownership patterns.

7. Cross-Origin SSO Example

A browser-based single sign-on flow may leave the application origin and interact with an identity provider. Cypress uses cy.origin() to execute commands in another origin. Wrap the complete authenticated outcome in cy.session(), and validate through the application after the redirect.

function loginWithSso(email: string, password: string) {
  return cy.session(['sso-login', email], () => {
    cy.visit('/login')
    cy.contains('button', 'Continue with SSO').click()

    cy.origin(
      'https://idp.example.test',
      { args: { email, password } },
      ({ email, password }) => {
        cy.get('[name=email]').type(email)
        cy.get('[name=password]').type(password, { log: false })
        cy.contains('button', 'Continue').click()
      },
    )

    cy.location('pathname').should('eq', '/dashboard')
  }, {
    validate() {
      cy.request('/api/me').its('status').should('eq', 200)
    },
  })
}

Replace the domain and selectors with the controlled test identity provider. Values passed to cy.origin() through args must be serializable. Keep credentials in protected configuration even though the sample accepts parameters.

External login UI often includes CAPTCHA, device challenges, or rate limits that are inappropriate for every application test. When the identity team provides an approved non-production programmatic flow, prefer it for setup and retain a small dedicated SSO journey. Do not bypass authentication through an undocumented cookie constructed by the test.

8. Keep Logout and Expiration Tests Separate

A healthy session helper is designed to recreate an invalid restored session. That is the opposite of what a logout or expiration test needs. If you call the helper again after clearing the cookie, validation can trigger setup and log the user back in. Test invalidation without immediately requesting the healthy cache again.

it('returns to login after logout', () => {
  loginByCookie('member@example.test', 'member')
  cy.visit('/account')

  cy.contains('button', 'Sign out').click()
  cy.location('pathname').should('eq', '/login')
  cy.getCookie('session_id').should('not.exist')

  cy.request({
    url: '/api/me',
    failOnStatusCode: false,
  }).its('status').should('eq', 401)
})

For expiration, use an approved test-support mechanism to issue an already expired token or advance server-controlled test time. Do not wait in real time or mutate a signed token arbitrarily. Assert both backend rejection and the browser's visible recovery behavior.

Avoid clearing all saved sessions as the feature action. Cypress.session.clearAllSavedSessions() controls the test runner cache, not the application's logout contract. The application must invalidate its cookie, token, or server session as documented.

9. Debugging Example for Creation and Restoration

When a cached login fails, determine which phase failed. Add temporary, non-sensitive log markers inside setup and validation. The Command Log and session instrument panel also identify created, restored, and recreated sessions.

function loginDebug(email: string) {
  return cy.session(['debug-login', email], () => {
    cy.log('creating browser session')
    cy.request('POST', '/api/auth/session', {
      email,
      password: 'secret-from-config',
    }).its('status').should('eq', 204)
  }, {
    validate() {
      cy.log('validating browser session')
      cy.request({
        url: '/api/me',
        failOnStatusCode: false,
      }).then((response) => {
        expect(response.status).to.eq(200)
        expect(response.body.email).to.eq(email)
      })
    },
  })
}

During local diagnosis, clear saved sessions once and rerun creation:

Cypress.session.clearAllSavedSessions()

Do not put that line in a normal beforeEach, because it removes the optimization and can conceal key collisions. If creation works but restoration fails, inspect cookie attributes, token expiry, origin, and validation side effects. If the wrong user appears, fix the ID inputs and check shared server data. If DOM commands run against a blank page, add cy.visit() after the helper.

Remove temporary logs after diagnosis, especially around sensitive identity flows. Preserve safe correlation IDs or endpoint status information in CI so authentication-service failures can be separated from UI defects.

10. Production Cypress cy.session Example Template

This consolidated template uses a cookie session, structured ID, exact validation, optional cross-spec reuse, and an explicit destination visit. It keeps domain context in the call and transport detail in one helper.

type AuthContext = {
  userKey: string
  role: 'member' | 'admin'
  tenantId: string
}

export function establishSession(
  context: AuthContext,
  cacheAcrossSpecs = false,
) {
  const { userKey, role, tenantId } = context

  return cy.session(
    ['test-auth', userKey, role, tenantId],
    () => {
      cy.request({
        method: 'POST',
        url: '/test-support/authenticate',
        body: context,
        log: false,
      }).then((response) => {
        expect(response.status, 'authentication setup').to.eq(204)
      })
    },
    {
      validate() {
        cy.request<AuthContext>({
          url: '/api/me/test-context',
          failOnStatusCode: false,
        }).then(({ status, body }) => {
          expect(status, 'authenticated identity').to.eq(200)
          expect(body).to.deep.equal(context)
        })
      },
      cacheAcrossSpecs,
    },
  )
}

it('creates a project as an administrator', () => {
  establishSession({
    userKey: 'admin-1',
    role: 'admin',
    tenantId: 'tenant-a',
  })
  cy.visit('/projects/new')
  cy.contains('h1', 'Create project').should('be.visible')
})

Before adopting the template, verify that the support endpoint is non-production, secure, and semantically equivalent to real app authentication. If callers vary cacheAcrossSpecs for the same ID within one run, Cypress treats the definitions as inconsistent. Use a separate helper or include the cache policy in a stable suite design rather than toggling it casually.

Run the helper twice in one spec to exercise restoration, across two specs when global caching is enabled, and on separate CI workers. Test one revoked session to prove validation rebuilds it. This small contract test catches most mistakes before hundreds of scenarios depend on the helper.

11. Verify the Helper Before Broad Adoption

Treat the session helper like shared infrastructure. Add a small contract spec that calls it twice for the same user, then asserts the second call reaches a protected route with the correct identity. Call it for a second role and tenant to prove IDs do not collide. Revoke one server session between calls and confirm validation causes setup to run again. These checks give fast evidence that creation, restoration, and recreation all work.

Run the contract spec with test isolation enabled and keep the destination visit outside the helper. Execute it in open mode, in headless mode, and on the same CI topology used by the full suite. A helper can work locally but fail in CI when secure cookie settings, domain names, clock skew, identity-provider quotas, or worker-specific configuration differ.

Monitor backend side effects. Login setup should not create a new permanent account on every restore attempt, and validation should never mutate business data. If test identities need provisioning, perform it through an idempotent support operation keyed by worker or tenant. Separate account ownership from browser session caching so a cache miss does not create uncontrolled records.

Document the state contract beside the helper: cookie or storage keys, identity endpoint, supported roles, tenant behavior, cache scope, and secret source. Do not document secret values. When the application authentication model changes, update this contract and its focused tests before feature specs. This small investment prevents a broken shared login from turning hundreds of unrelated scenarios red.

Finally, keep one uncached login smoke test. It confirms the authentication entry point still works from a clean browser even when most feature specs use restored state. The cached helper and the uncached smoke test answer different questions, and together they protect both suite speed and real login coverage.

Interview Questions and Answers

Q: Show a minimal cy.session() login example.

I pass a deterministic ID and a setup callback that completes login, then add validation against a protected endpoint. After the helper, I visit the test route. The ID excludes the password and includes any role or tenant that changes the state.

Q: How do you store a token in local storage?

I request the token, visit the application origin, and use the visit onBeforeLoad callback to set the exact key before the app initializes. I then validate a protected endpoint. I do not invent a storage format different from the real application.

Q: Can two users be used in one spec?

Yes. Give each state a unique ID based on user and other authorization context. Each call restores its own cookies and web storage, but shared backend mutations still require isolated data.

Q: What validation would you avoid?

I avoid destructive writes, slow dashboards, and cookie-existence-only checks. A protected identity GET that confirms user, tenant, and permission is faster, idempotent, and more precise.

Q: Why not cache logout tests?

The healthy helper is designed to rebuild invalid state. Calling it after logout can authenticate the user again and defeat the scenario. I establish state once, perform logout, then assert browser and backend invalidation without restoring the cache.

Q: How does cross-origin SSO fit?

I run the identity-provider interaction through cy.origin() inside session setup, assert the application redirect, and validate through the app's protected endpoint. If an approved programmatic test flow exists, I usually prefer it for non-SSO feature specs.

Q: How do you prove caching works?

I observe one creation and a later restoration for the same ID, while validation runs both times. For cross-spec caching, I verify the behavior in one run on one machine and expect a fresh setup on another machine or run.

Common Mistakes

  • Ending UI setup immediately after clicking Sign in, before authenticated state is stable.
  • Using the same ID for cookie login and token login.
  • Including the password or access token in a visible session ID.
  • Validating only cookie existence instead of the expected identity.
  • Setting local storage on an unintended origin.
  • Forgetting cy.visit() after restoration with test isolation enabled.
  • Toggling cacheAcrossSpecs for callers that otherwise use the same session.
  • Expecting one global cache to cross CI machines or test runs.
  • Calling the healthy login helper again inside a logout assertion.
  • Clearing all cached sessions in every beforeEach.

Conclusion

A strong cypress cy.session example matches the application's real authentication model and makes state identity explicit. Use UI setup only when it adds value, prefer approved API setup for routine preconditions, validate the exact user and permissions, and keep navigation in the test.

Choose one recipe, adapt its routes and storage contract, then test creation, restoration, invalidation, and parallel execution before rolling it across the suite. That disciplined rollout delivers fast login reuse without trading away test isolation or security evidence.

Interview Questions and Answers

Give an API cookie cy.session example design.

I use an ID containing login strategy, user, role, and tenant. Setup posts to the approved auth endpoint and asserts the session cookie or response. Validation calls a protected identity endpoint and confirms the same context, then the test visits its route.

How would you cache a localStorage token?

I obtain it from the real test authentication contract, visit the application origin, and set the exact storage key before app initialization. I treat the token as opaque and keep it out of logs and IDs. A protected endpoint validates the state.

How do you switch users safely?

I give each user and authorization context a distinct deterministic ID. Cypress restores the matching cookies and storage when that ID is requested. I also isolate mutable server-side data because the session snapshot does not roll backend state back.

What should cacheAcrossSpecs tests prove?

They should prove one shared helper is used consistently, restoration works in another spec on the same machine and run, and validation still detects revoked state. The CI design must expect each parallel machine to create its own cache.

How do you test logout with a cached login?

I use the cache only to establish the initial healthy state. After the browser logs out, I assert the cookie is gone, the protected API returns 401, and the UI returns to login. I do not invoke the session helper again within that scenario.

What is wrong with validating only a cookie?

The cookie can be expired, revoked, malformed, or associated with the wrong user. A protected identity request proves server acceptance and can verify role, permission, and tenant. Cookie existence is only a setup diagnostic.

How do you troubleshoot a blank page after session restore?

I confirm test isolation is enabled and add an explicit cy.visit after the session helper. Restored state does not include the page. I wait for a real application readiness condition rather than adding a fixed delay.

Frequently Asked Questions

What is the simplest cy.session login example?

Call cy.session with a stable user ID, perform a complete login in setup, and validate a protected endpoint. After the helper returns, visit the route the test exercises.

Can cy.session cache localStorage tokens?

Yes. cy.session preserves localStorage, but the token must be written under the application origin and in the exact format the app expects. Validate the resulting authentication through the backend.

How do I use cy.session for multiple roles?

Include the user and role in the session ID, and validate the role or required permission. If tenant also changes authorization, include tenant in the ID and validation.

Can cy.session work with SSO?

Yes. A browser SSO flow can run inside setup, using cy.origin for commands on another origin when required. A controlled programmatic authentication path is often more reliable for routine feature tests.

Should logout tests call cy.session again?

Not after the logout action. The healthy session helper may recreate invalid state and sign the user back in. Establish the session once, log out, and verify invalidation directly.

Why is cy.visit needed after cy.session?

The cache restores cookies and storage, not a rendered page. Test isolation can leave the browser on a blank page, so the test should explicitly visit its destination.

How do I know whether setup or validation failed?

Inspect the session entry in the Cypress runner and Command Log to see creation, restoration, or recreation. Temporary non-sensitive log markers can separate setup from validation during diagnosis.

Related Guides