Skip to content

Testing Login Flows in Cypress

The login page itself deserves focused, thorough test coverage since it's the entry point to nearly everything else. This lesson covers testing valid and invalid login scenarios, validation, and account lockout behavior.

What a Complete Login Test Suite Covers

Beyond the "happy path" of a successful login, a thorough login test suite covers invalid credentials, empty/malformed input validation, and any rate-limiting or account lockout behavior the application implements after repeated failures.

describe('Login', () => {
  it('logs in successfully with valid credentials', () => {
    cy.visit('/login');
    cy.get('[data-cy="email"]').type('user@example.com');
    cy.get('[data-cy="password"]').type('correct-password');
    cy.get('[data-cy="submit"]').click();
    cy.url().should('include', '/dashboard');
  });
});

This happy-path test is only the starting point, real coverage needs several more scenarios alongside it.

Login Test Scenarios to Cover

1. Valid credentials -> successful login and redirect
2. Invalid password -> clear error message, stays on login page
3. Unknown email -> generic error (avoid leaking account existence)
4. Empty fields -> client-side validation errors
5. Repeated failures -> rate limiting / lockout behavior
  • Test both the success path and each meaningful failure path separately.
  • Confirm error messages don't leak whether a specific email exists (a security best practice).
  • Verify client-side validation prevents submission of obviously invalid input.
  • If lockout/rate-limiting exists, test that it actually triggers after the configured threshold.

Login Test Scenarios Cheat Sheet

A checklist of login scenarios worth covering in most applications.

Scenario Expected Behavior
Valid email + password Redirects to authenticated area
Valid email + wrong password Shows a generic invalid-credentials error
Unregistered email Shows the same generic error (no account-existence leak)
Empty email/password Client-side validation error, no request sent
Malformed email format Client-side validation error
Too many failed attempts Rate limiting or temporary lockout message
Already logged in, visits /login Redirects away from login automatically

Testing Invalid Credentials Correctly

A well-designed login error message reveals as little as possible about why a login failed, showing the same generic message whether the email doesn't exist or the password is simply wrong. Testing this consistency directly guards against an accidental security regression.

it('shows a generic error for invalid credentials, regardless of cause', () => {
  cy.visit('/login');
  cy.get('[data-cy="email"]').type('nonexistent@example.com');
  cy.get('[data-cy="password"]').type('anything');
  cy.get('[data-cy="submit"]').click();
  cy.get('[data-cy="login-error"]').should('contain', 'Invalid email or password');
});

Testing Rate Limiting or Account Lockout

If your application locks an account (or throttles attempts) after repeated failures, stubbing the backend response for that specific scenario is often more reliable than actually triggering real lockout logic against a live backend during a test run.

cy.intercept('POST', '/api/login', {
  statusCode: 429,
  body: { message: 'Too many attempts. Try again in 5 minutes.' },
}).as('loginRateLimited');

cy.get('[data-cy="submit"]').click();
cy.wait('@loginRateLimited');
cy.get('[data-cy="login-error"]').should('contain', 'Too many attempts');

Common Mistakes

  • Only testing the successful login path and skipping invalid-credential and validation scenarios entirely.
  • Revealing whether an email exists through different error messages, and never having a test that would catch this regression.
  • Attempting to trigger real rate-limiting/lockout logic repeatedly against a live backend instead of stubbing it.
  • Not testing that an already-authenticated user is redirected away from the login page automatically.

Key Takeaways

  • A complete login test suite covers success, invalid credentials, validation, and lockout scenarios.
  • Generic error messages for invalid credentials should be tested explicitly as a security safeguard.
  • Stubbing rate-limiting responses is more reliable than triggering real lockout logic during tests.
  • Redirect behavior for already-authenticated users visiting the login page is worth its own test.

Pro Tip

Add a dedicated test asserting that the login error message is character-for-character identical for both "wrong password" and "unknown email" scenarios, this is a security property that is easy to accidentally break during an unrelated refactor, and a direct test is the cheapest way to guard it.