Authenticate Faster in Tests with the cy.session Command

August 4, 2021

•

By The Cypress Team

Cypress helped you reduce the time it takes to log in before each of your tests with the introduction of the experimental cy.session() command with Cypress 8.2.0.

To view the latest updates, including our fix for the issue with the cy.session command in Cypress 12.1.0, view our changelog.

A Brief Refresher on Logging in

When testing an application that requires authentication, it's common to create a cy.login() custom command or login helper function to log in to your app:

Cypress.Commands.add('login', (username, password) => {
  cy.visit('/login')
  cy.get('[data-test=username]').type(username)
  cy.get('[data-test=password]').type(password)
  cy.get('#login').click()
  cy.url().should('contain', '/login-successful')
})

In order to ensure your tests can access the app, you need to call cy.login() to log in before each test. Often, this is done in a beforeEach hook:

describe('User account page', () => {
	
  beforeEach(() => {
    cy.login('user1', 'p4ssw0rd123')
  })
	
  it('should actually be accessible', () => {
    cy.visit('/account')
  })

  it('should have the correct page title', () => {
    cy.visit('/account')
    cy.title().should('eq', 'Account Details for User 1')
  })
  
})

While logging in during each test that requires being logged-in is a best practice, the process of logging in can be slow, which people sometimes attempt to work around by logging in just once per spec file in a before hook, or by using the Cypress.Cookies API to persist cookies across tests. However, having tests rely on the state of previous tests is not a best practice, and should be avoided.

Caching Sessions When Logging in

The new cy.session() command solves this problem by caching and restoring cookies, localStorage and sessionStorage after a successful login. The steps that your login code takes to create the session will only be performed once when it's called the first time in any given spec file. Subsequent calls will restore the session from cache.

You can easily update your cy.login() custom command or login helper function to use cy.session(), like so:

Cypress.Commands.add('login', (username, password) => {
  cy.session([username, password], () => {
    cy.visit('/login')
    cy.get('[data-test=username]').type(username)
    cy.get('[data-test=password]').type(password)
    cy.get('#login').click()
    cy.url().should('contain', '/login-successful')
  })
})

Note that, in order to ensure sessions are created consistently, Cypress clears the page after cy.session() is called, so cy.visit() will always need to be called explicitly afterwards.

Fortunately, explicitly calling cy.visit() makes your tests more readable. And because cy.session() caches session data, your tests won't need to rely on external services and UI interaction nearly as much, so they'll run faster.

Switching Users in Tests

When you run cy.session(), it automatically clears the active session before running your login code, so that in addition to speeding up the process of logging in, switching between users in a single test will be faster, because you no longer need to log out explicitly before logging in a different user.

it('should transfer money between users', () => {
  cy.login('user', 's3cr3t')
  cy.visit('/account')
  cy.get('#amount').type('100.00')
  cy.get('#send-money').click()

  // you don't need to log out here!
  
  cy.login('other-user', 'p4ssw0rd')
  cy.visit('/account')
  cy.get('#balance').should('eq', '100.00')
})

Because users no longer need to be logged out, and because the active session is getting completely cleared and restored, switching between users in a test can behave more like multiple users using different browsers, rather than multiple users taking turns in the same browser, which allows your tests to more closely resemble real-world usage.

Of course, you can continue to log out any time you need to, as cy.session() will automatically detect invalid sessions, using a validate function that you define.

Using the Session Command

Once you've upgraded to Cypress 8.2.0, you can start using this feature by setting the experimentalSessionSupport configuration option to true.

Be sure to check out the cy.session() docs for more details, the changelog for a full list of fixes, and the migration guide to assist with your upgrade.

One More Thing — Avoiding CORS Errors

It’s not a problem that comes up frequently, but if you get a CORS error when you run your tests, it might be caused by the absence of authentication. The security measure kicks in if the cross-origin request is not authenticated — all the more reason to ensure the right steps are in place to authenticate faster and more accurately. You can learn more about CORS and how it interacts with Cypress here.

We'd like to hear from you!

The Cypress team has been working hard to deliver this improved experience. We're excited to bring these new APIs to Cypress users, and as always, we're eager to hear your feedback.

You can submit an issue on Github or chat with us on our Discord. Especially while this feature is experimental, issue submissions are critical. If you’d like to stay up to date with all things Cypress, subscribe to our newsletter.

Thanks for your support, and happy testing!

-Cypress Team