Why Logging In Through the UI Is Slow
If every test starts by filling in a login form, clicking submit, and waiting for a redirect, that overhead is repeated hundreds of times across a suite, and the login page itself becomes a single point of failure that can make unrelated tests fail. Cypress's official recommendation is to bypass the UI for authentication in every test except the ones that specifically test login itself, and instead establish an authenticated session programmatically before the test's real assertions begin, which is both faster and more resilient to unrelated login-page changes.
Cricket analogy: It's like a team not re-running the full pre-match warm-up routine before every single net session — they get straight to the specific drill they need to practice, saving time across many sessions.
Programmatic Login
The most direct approach is to call the backend's authentication endpoint using cy.request(), which makes a real HTTP request outside the browser's normal page-load lifecycle and returns the response synchronously in the test, for example cy.request('POST', '/api/login', { email, password }).then((res) => window.localStorage.setItem('authToken', res.body.token)). For cookie-based auth, you would instead use cy.setCookie('session_id', res.body.sessionId) so the cookie is present before cy.visit() loads the app, meaning the application believes the user is already logged in.
Cricket analogy: It's like a broadcaster injecting a pre-recorded pitch report directly into the feed instead of sending a camera crew out to film it live every single time, getting the same end result faster.
cy.session() (available since Cypress 9.6+) is the recommended mechanism for caching an authenticated session across tests: you define a named session with a setup function containing the programmatic login logic, and Cypress restores cookies, localStorage, and sessionStorage from cache on subsequent calls with the same session ID instead of re-running the setup function, dramatically speeding up suites. An optional validate callback lets you assert the session is still valid (for example, checking that a protected API call returns 200) before reusing cached state, and Cypress automatically re-runs setup if validation fails.
Cricket analogy: It's like a groundskeeper preparing a pitch once at the start of the day and reusing that same prepared surface for multiple net sessions, only re-preparing it if a quick inspection shows it's degraded.
Third-Party and SSO Authentication
For applications using OAuth or SSO providers like Google or Okta, testing the actual third-party login page is fragile and against most providers' terms of service for automation. Instead, teams typically stub the OAuth callback with cy.intercept() to return a fake authorization code and mock token exchange response, or use cy.origin() to briefly step into the identity provider's domain when a real staging IdP account is available, since Cypress commands normally cannot interact with a different origin than the one under test without cy.origin() wrapping that cross-origin block.
Cricket analogy: It's like a broadcaster not re-filming a rival network's studio segment live but instead using an agreed pre-cleared clip feed, since directly operating in someone else's studio isn't practical or allowed.
// cypress/support/commands.js
Cypress.Commands.add('loginByApi', (email, password) => {
cy.session(
[email, password],
() => {
cy.request('POST', '/api/login', { email, password }).then((res) => {
window.localStorage.setItem('authToken', res.body.token);
});
},
{
validate() {
cy.request({
url: '/api/me',
headers: { Authorization: `Bearer ${window.localStorage.getItem('authToken')}` },
}).its('status').should('eq', 200);
},
cacheAcrossSpecs: true,
}
);
});
// cypress/e2e/dashboard.cy.js
describe('Dashboard', () => {
beforeEach(() => {
cy.loginByApi('user@example.com', 'Secret123!');
cy.visit('/dashboard');
});
it('shows the welcome banner', () => {
cy.contains('Welcome back').should('be.visible');
});
});cy.session() supports a cacheAcrossSpecs: true option, which persists the cached session across every spec file in the run, not just within one file — combined with a fast validate check, this can turn a suite's cumulative login time from minutes into seconds.
Avoid automating a real third-party identity provider's hosted login page (Google, Microsoft, GitHub OAuth screens). It is brittle due to CAPTCHAs and bot detection, and automating login against most providers violates their terms of service — stub the callback or use a dedicated test IdP account instead.
- Bypass the UI login form for every test except ones specifically testing login itself.
- cy.request() can call a login API endpoint directly and return the auth token or cookie synchronously.
- cy.session() caches cookies, localStorage, and sessionStorage across tests, keyed by an identifier you choose.
- A validate callback in cy.session() confirms cached auth is still good before reuse, re-running setup if not.
- cacheAcrossSpecs: true persists a session across the entire spec run, not just one file.
- Avoid automating third-party OAuth/SSO login pages directly; stub the callback or use a dedicated test IdP account.
- cy.origin() is required to interact with a different domain than the one under test, such as a real staging IdP.
Practice what you learned
1. What is the recommended approach for authenticating in every Cypress test that is not specifically testing the login flow?
2. What does cy.session()'s validate callback do?
3. What does the cacheAcrossSpecs: true option on cy.session() enable?
4. Why should you avoid automating a real Google or Microsoft OAuth login page in Cypress tests?
5. What Cypress command is required to interact with a domain different from the application under test, such as a real staging identity provider?
Was this page helpful?
You May Also Like
Environment Variables and Configuration
Learn how Cypress resolves environment variables across config files, OS variables, and the CLI, and how fixtures supply deterministic test data.
Testing File Uploads
Learn how Cypress's built-in selectFile() command tests file inputs and drag-and-drop uploads, and how to verify the resulting network request.