Q3 of 48 · Cypress

How do you handle authentication efficiently in a large Cypress suite?

CypressSeniorcypressauthenticationperformancecy-session

Short answer

Short answer: Use cy.session() to cache login state per user role, hit the API directly for the login itself, and avoid logging in through the UI in every test. This typically cuts suite runtime by 40–70%.

Detail

In a real suite, logging in through the UI in every test is the single biggest source of wasted time. The fix is a layered strategy.

First, always log in via the API, not the UI. UI login tests their own thing once; every other test just needs a valid session cookie. A custom command that hits POST /api/login and writes the resulting cookie is dramatically faster and more reliable.

Second, cache the session with cy.session(). Cypress will run the setup function once per unique session ID and restore the cookies/local storage on subsequent calls. Pass the user role or email as the key so different personas don't collide. Add a validate callback to verify the cached session is still good — when it isn't, Cypress re-runs setup.

Third, scope the cache by role, not by test. If your tests use 4 personas (admin, manager, viewer, anonymous), you log in 4 times across the entire suite, not once per spec.

Edge cases worth mentioning: testIsolation: true (the default in Cypress 12+) clears state between tests, so cy.session is the only sane way to persist auth. For multi-domain SSO flows you may need cy.origin plus session caching, and for token-based auth you can store the JWT in localStorage instead of relying on cookies.

// EXAMPLE

support/commands.ts

Cypress.Commands.add('loginAs', (role: 'admin' | 'viewer') => {
  cy.session(
    role,
    () => {
      cy.request({
        method: 'POST',
        url: '/api/login',
        body: {
          email: Cypress.env(`${role}Email`),
          password: Cypress.env('password'),
        },
      }).then((res) => {
        window.localStorage.setItem('token', res.body.token);
      });
    },
    {
      validate() {
        cy.request('/api/me').its('status').should('eq', 200);
      },
      cacheAcrossSpecs: true,
    },
  );
});

// usage in a test
beforeEach(() => {
  cy.loginAs('admin');
  cy.visit('/dashboard');
});

// WHAT INTERVIEWERS LOOK FOR

Understanding that UI login is the wrong tool for setup, awareness of cy.session and cacheAcrossSpecs, and ability to reason about role-based caching. Mentioning the validate callback signals real production experience.

// COMMON PITFALL

Suggesting fixtures or 'just disable auth in tests' — both create divergence from production. Or using cy.session without a validate callback and getting stale-session flakes in CI.