Q15 of 48 · Cypress
When would you use cy.session over a custom login command?
Short answer
Short answer: `cy.session` caches the logged-in state across tests (and optionally specs), avoiding re-login for every test. A plain custom command logs in every time. Use `cy.session` whenever the same user logs in repeatedly — it can cut suite time by 40-70%.
Detail
A naive custom command logs in on every beforeEach:
Cypress.Commands.add('login', (email) => {
cy.request('POST', '/api/login', { email }).then(({ body }) => {
window.localStorage.setItem('token', body.token);
});
});
That works but pays the login cost on every test. cy.session adds a caching layer:
Cypress.Commands.add('login', (email) => {
cy.session(email, () => {
cy.request('POST', '/api/login', { email }).then(({ body }) => {
window.localStorage.setItem('token', body.token);
});
});
});
Cypress runs the setup function once per unique session ID (the first argument), captures cookies + localStorage + sessionStorage, and restores them on subsequent calls. Pass the user role/email as the key so different personas don't collide.
Two flags worth knowing:
cacheAcrossSpecs: truepersists the session across spec files. With test isolation enabled (default), each spec otherwise re-runs setup; this flag avoids that.validatecallback — runs on every restore; if it throws or returns false, Cypress re-runs setup. Use it to verify the cached session is still valid (e.g., token not expired).
Use plain custom commands for one-off auth flows or when the user changes per test. Use cy.session whenever the same user is reused.
// 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(({ body }) => {
window.localStorage.setItem('token', body.token);
});
},
{
cacheAcrossSpecs: true,
validate() {
cy.request('/api/me').its('status').should('eq', 200);
},
},
);
});
// In specs:
beforeEach(() => {
cy.loginAs('admin');
cy.visit('/dashboard');
});