Learn to use cy.session()

Here is a step-by-step guide on how to use cy.session(). Run Cypress cy.session Tests on Real Devices with BrowserStack.

Get Started free
Home Guide How to Use cy.session()

How to Use cy.session()

By Priyanka Bhat, Community Contributor -

Cypress is one of the popular end-to-end test automation tools. It offers unique features such as advanced debugging, video recording of execution, and screenshot-based time travel.

Unlike traditional automation tools, Cypress follows a different architecture. Cypress doesn’t have middleware drivers like Selenium; it directly interacts with the browser. Because of this, Cypress tests are more stable and faster than those of other test automation frameworks. Cypress also offers advanced capabilities like caching and restoring sessions with the cy.session() function.

What is cy.session ()?

cy.session() is a function or command in Cypress that allows caching and restoring browser session data such as cookies, local storage, session storage, etc. across the tests.

This technique helps speed up test execution, as the user doesn’t need to log in multiple times when he wants to switch between types of users.

When to use cy.session ()

Cy.session() will be helpful in multiple scenarios, such as faster execution, using the same session in different types, etc.

Below are some of the general use cases for Cypress session usage.

  • Multiple login scenarios: If your tests require login in different test cases, logging into the application and performing the action may take considerable time each time. You can use a Cypress session to log in once, store the session data, and use it across the tests.
  • Maintaining state: Some tests may require a persistent state across the tests. The Cypress session helps to achieve this by storing the state of the logged-in user.
  • Session validation: Cypress session helps ensure the validity of the session, such as cookie and token validity.
  • Improve the test suite performance: If your test case requires repeated logins, logging into the application takes a lot of time, which contributes to the delay in execution. In such scenarios, Cypress sessions will be useful in improving performance.

Example of cy.session () in Cypress

Consider two scenarios as listed below:

  1. Log in as a user and verify user can add the product to the cart
  2. Log in as a user and verify user can view the history of orders

Considering the above two scenarios, the cypress tests look like given below.

Note: The below code does not use sessions

/// <reference types="cypress" />

describe('Session usage example', () => {

    beforeEach(() => {

        cy.visit("https://bstackdemo.com/");

        cy.get('#signin').click();

        cy.get('#username').click();

        cy.get('#username').type("existing_orders_user")

        cy.get('body').type("{Enter}")

        cy.get('#password').type("testingisfun99")

        cy.get('body').type("{Enter}")

        cy.get("#login-btn").click();

    })

    it('add product to cart', () => {

        cy.get("div[data-sku='iPhone12-device-info.png'] div").contains("Add to cart").first().click()

        cy.get('p[class="title"]').should('have.text', 'iPhone 12')

    });

    it('view past orders', () => {

        cy.get('#orders').click();

        cy.get("div[class='item-view-left-col-inner']").should('have.length.at.least', 10);

    });

});

In the above example,

beforeEach() function has the code to log in to the application. This code will be run before every test. Subsequent it() function has two scenarios, add product to card and view past orders.

As you can see logging into the application before each scenario takes some and it can cause a performance delay.

The above script can be modified with sessions like below.

Note: The below code uses cypress session

/// <reference types="cypress" />

describe('Session usage example', () => {

    beforeEach(() => {

        var name = "existing_orders_user"

        cy.session(name, () => {

            cy.visit("https://bstackdemo.com/");

            cy.get('#signin').click();

            cy.get('#username').click();

            cy.get('#username').type(name)

            cy.get('body').type("{Enter}")

            cy.get('#password').type("testingisfun99")

            cy.get('body').type("{Enter}")

            cy.get("#login-btn").click();

        },

            {

                validate() {

                  cy.get('span[class="username"]').should('have.text','existing_orders_user')

                },

            }

        );

    })

    it('add product to cart', () => {

        cy.visit("https://bstackdemo.com");

        cy.get("div[data-sku='iPhone12-device-info.png'] div").contains("Add to cart").first().click()

        cy.get('p[class="title"]').should('have.text', 'iPhone 12')

    });

    it('View Past orders', () => {

        cy.visit("https://bstackdemo.com");

        cy.get('#orders').click();

        cy.get("div[class='item-view-left-col-inner']").should('have.length.at.least', 10);

    });

});

The above code is using cy.session() in beforeEach() function.

Cy.session() will be created when you run the test for the first time. It will try to restore the session in subsequent tests and validate whether it was successful using the validate function().

Considering the above example, when running the ‘add product to cart‘ test, there will not be a session, so it will recreate the session. When it executes the ‘view past orders‘ test, a session is already created.

It doesn’t execute the login function again instead, it restores the previous session and performs the action.

Output of using cy.session() in beforeEach() function

Output of cysession in before each function

How to Use cy.session ()

There are different ways to use the Cypress session function such as programmatic login, helper function, and custom command.

Below is an overview of each of them.

Updating an existing login custom command

Cypress allows you to create a custom command, considering you already have a custom command for LoginExistingOrderUser

Note: To create a custom command you need to add the code under cypress/support/commands.js file.

For more details, refer to Cypress custom command documentation.

Your existing Login command

  Cypress.Commands.add('login', (username, password) => {

        cy.visit("https://bstackdemo.com/");

        cy.get('#signin').click();

        cy.get('#username').click();

        cy.get('#username').type(username)

        cy.get('body').type("{Enter}")

        cy.get('#password').type(password)

        cy.get('body').type("{Enter}")

        cy.get("#login-btn").click();

  })

If you want to reduce the login attempts for each time when you run the cypress tests, you can use the cypress session function.

Update the login command as below.

Cypress.Commands.add('login', (username, password) => {

    cy.session([username, password], () => {

        cy.visit("https://bstackdemo.com/");

        cy.get('#signin').click();

        cy.get('#username').click();

        cy.get('#username').type(username)

        cy.get('body').type("{Enter}")

        cy.get('#password').type(password)

        cy.get('body').type("{Enter}")

        cy.get("#login-btn").click();

    })

  })

In the above example, cy.session() in being used in the first line, which creates a session for this user initially. Later whenever it is called, the same session will be used for login.

Create a custom command with Session Validation

One of the major issues with the above function is that Cypress tries to restore the session. However, the validity of the session is not guaranteed. There may be a chance that the session might have expired or been invalid for some reason.

To validate the integrity, Cypress allows validation of the session with the validate() function.

The above session code can be altered as below to validate the session.

Cypress.Commands.add('login', (username, password) => {

    cy.session([username, password], () => {

        cy.visit("https://bstackdemo.com/");

        cy.get('#signin').click();

        cy.get('#username').click();

        cy.get('#username').type(username)

        cy.get('body').type("{Enter}")

        cy.get('#password').type(password)

        cy.get('body').type("{Enter}")

        cy.get("#login-btn").click();

    },

        {

            validate() {

                cy.get('span[class="username"]').should('have.text', username)

            }

        }

    );

})

In the above code, validate() function is used to ensure that the login name matches the logged Centennial username. If the session has expired, the validate() function fails, prompting Cypress to automatically recreate the session.

Updating an existing login helper function

The helper function is another way to increase code reusability.

Consider that you have created a login helper function to perform the login action below.

const login = (username, password) => {

            cy.visit("https://bstackdemo.com/");

            cy.get('#signin').click();

            cy.get('#username').click();

            cy.get('#username').type(username)

            cy.get('body').type("{Enter}")

            cy.get('#password').type(password)

            cy.get('body').type("{Enter}")

            cy.get("#login-btn").click();

        } 

As you can see, the above function does not use a Cypress session; it accepts a username and password and tries to perform the login function.

When you call the above function in tests, it will try to log in multiple times, as the session will not be stored.

The above function can be modified with cy.session() function as below

    const login = (username, password) => {

        cy.session([username, password], () => {

            cy.visit("https://bstackdemo.com/");

            cy.get('#signin').click();

            cy.get('#username').click();

            cy.get('#username').type(username)

            cy.get('body').type("{Enter}")

            cy.get('#password').type(password)

            cy.get('body').type("{Enter}")

            cy.get("#login-btn").click();

        },

            {

                validate() {

                    cy.get('span[class="username"]').should('have.text', username)

                }

            }

        );

    }

In the above code same login helper function is modified with cy.session() function.

The cy.session() function is inserted in the first line, and the validate function is used at the end to verify the session restoration. If the session is expired or invalid, a new session is automatically created.

Switching sessions inside tests

Consider an example of an admin user who modifies a few action items that you need to validate by logging in as a limited privileged feature.

In both of these scenarios, you need to perform single tests.

This test case looks complicated; but can be easily achieved with the Cypress Session function by storing the sessions.

For example, take an example of a scenario.

Step 1: Demo user login to the system and checks if the product is showing for him

Step 2: The secondary user login to the system and checks if order history is visible for him

Step 3: Demo user again logs in and checks order history is not visible for him

Start by creating the login helper function with the session.

 const login = (name) => {

        cy.session(name, () => {

            cy.request({

                method: 'POST',

                url: 'https://bstackdemo.com/api/signin',

                body: { userName: name, password: 'testingisfun99' },

            }).then(() => {

                window.sessionStorage.setItem('username', name)

            })

        })

    }

The above login helper function uses the programmatic (API-based) login approach by sending a request to the sign-in API with valid credentials.

After receiving the response, the user session details are stored in the browser’s temporary storage using the username property.

You can create the script to simulate the above scenario, which looks like below.

it('demo Switch user', () => {

        //Demo user login

        login("demouser", "testingisfun99");

        cy.visit("https://bstackdemo.com");

        cy.get("div[data-sku='iPhone12-device-info.png'] div").contains("Add to cart").first().should('exist')

        //Existing order user login

        login("existing_orders_user", "testingisfun99");

        cy.visit("https://bstackdemo.com");

        cy.get('#orders').click();

        cy.get("div[class='item-view-left-col-inner']").should('have.length.at.least', 10);

        // Demo user login

        login("demouser", "testingisfun99");

        cy.visit("https://bstackdemo.com");

        cy.get('#orders').click();

        cy.get("h2").should('have.text','No orders found')

    });
  • First demo user logs in and checks if at least one item is available to add to the cart.
  • Second, existing_orders_user logs in and checks if his history count is at least 10
  • Third, the demo user again logs in and checks if the order history page has no orders found.

All these things will be done in Cypress in a few seconds. It doesn’t log in repeatedly; rather it stores the session details and restores them.

Putting everything together.

describe('Demo switch user', () => {

    const login = (name) => {

        cy.session(name, () => {

            cy.request({

                method: 'POST',

                url: 'https://bstackdemo.com/api/signin',

                body: { userName: name, password: 'testingisfun99' },

            }).then(() => {

                window.sessionStorage.setItem('username', name)

            })

        })

    }

    it('demo Switch user', () => {

        //Demo user login

        login("demouser", "testingisfun99");

        cy.visit("https://bstackdemo.com");

        cy.get("div[data-sku='iPhone12-device-info.png'] div").contains("Add to cart").first().should('exist')

        //Existing order user login

        login("existing_orders_user", "testingisfun99");

        cy.visit("https://bstackdemo.com");

        cy.get('#orders').click();

        cy.get("div[class='item-view-left-col-inner']").should('have.length.at.least', 10);

        // Demo user login

        login("demouser", "testingisfun99");

        cy.visit("https://bstackdemo.com");

        cy.get('#orders').click();

        cy.get("h2").should('have.text','No orders found')

    });

});

Output

Output for Switching sessions inside tests

Modifying session data before caching

Cypress also allows you to modify the session data before caching. localStorage and sessionStorage can be used for this purpose.

You can add or remove the session data before caching and the data you want to cache.

    const login = (username, password) => {

        cy.session([username, password], () => {

            cy.visit("https://bstackdemo.com/");

            cy.get('#signin').click();

            cy.get('#username').click();

            cy.get('#username').type(username)

            cy.get('body').type("{Enter}")

            cy.get('#password').type(password)

            cy.get('body').type("{Enter}")

            cy.get("#login-btn").click();

    cy.clearCookie('userid')

      cy.window().then((win) => {

                win.localStorage.removeItem('token')

          })

     cy.setCookie('session_id', 'a8498djmsurm33323')

        });

    }

In the example above, the item is removed from the cookie using the clearCookie() function. Similarly, unnecessary data is cleared from local storage using the win.localStorage.removeItem() function.

The cookie is set with the session ID using the same function. This is just an example, and the addition or removal of information can be adjusted based on the application’s behavior.

Caching session data across specs

By default, Cypress doesn’t cache the sessions across the spec files. If you have such requirements, you can achieve them through the flag cacheAcrossSpecs=true.

You need to add this flag within the cy.session() function, as shown below.

    const login = (username, password) => {

        cy.session([username, password], () => {

            cy.visit("https://bstackdemo.com/");

            cy.get('#signin').click();

            cy.get('#username').click();

            cy.get('#username').type(username)

            cy.get('body').type("{Enter}")

            cy.get('#password').type(password)

            cy.get('body').type("{Enter}")

            cy.get("#login-btn").click();

        },

            {

                validate() {

                    cy.get('span[class="username"]').should('have.text', username)

                },

                cacheAcrossSpecs: true,

            }

        );

    }

In the above code, you can see that cacheAcrossSpecs: true is mentioned after the session validation. Once you have this code, you can use them in different spec files.

Like shown below:

// ordres.cy.js

it('can view orders, () => {

  login()

})

//placeorders.cy.js

it('can create a order', () => {

  login()

})

Common Errors While Using cy.session()

  • Cypress cy.session() may not cache all the related information for the session at once. To avoid such a situation, you can simply add the guarding assertion at the end.
cy.session('login', () => {

   cy.visit('/login')

   cy.get('[data-cy="login "]')

      .type('example@browserstack.com')

   cy.get('[data-cy=" password"]')

      .type('Password@123')

   cy.location('pathname')

      .should('eq', '/')

})
  • Use the validate function always to ensure the session validity, often session will be invalid due to expiry adding the validate function helps to recreate the session if it is invalid.
cy.session('login', () => {

   cy.visit('/login')

   cy.get('[data-cy="login "]')

      .type('example@browserstack.com')

   cy.get('[data-cy=" password"]')

      .type('Password@123')

   cy.location('pathname')

      .should('eq', '/')

}, {

    validate() {

      cy.document()

        .its('cookie')

        .should('contain', 'state')

    }

  })
  • The cookie might not store as expected. In such scenarios, you can assert the cookie values to be exists.
cy.session('login', () => {

   cy.visit('/login')

   cy.get('[data-cy="login "]')

      .type('example@browserstack.com')

   cy.get('[data-cy=" password"]')

      .type('Password@123')

   cy.location('pathname')

      .should('eq', '/')

cy.document()

        .its('cookie')

        .should('contain', 'token')

   })

})

Best Practices of using cy.session () in Cypress

Below are some of the best practices to follow while using the cy.session()

  • Always use the validate function to reduce errors.
  • Use the cacheAcrossSpecs flag only if required. Otherwise, this may result in false negatives, as cache restoring is applicable globally.
  • Use the programmatic way of login, which makes the test execution faster and more reliable.
  • Avoid manipulating session details unless required, as this can bypass real-user scenarios.
  • Use cloud testing tools like BrowserStack to run parallel tests on real devices in real user conditions.

Talk to an Expert

Why Run Cypress cy.session Tests on Real Devices with BrowserStack

Though Cypress is the most popular test automation tool, it has many limitations. However, such limitations can be easily overcome by using cloud testing tools like BrowserStack.

BrowserStack Automate supports integration with many frameworks, including Cypress. Below are some of the advantages of running Cypress tests in BrowserStack.

  • Cypress does not allow parallel execution locally: Using BrowserStack, you can run multiple tests parallelly with Cypress. It also reduces the execution duration.
  • Run the tests like real users: Real device cloud provides access to a broad spectrum of devices and environments, ensuring tests reflect actual user conditions accurately.
  • Security standards: Browserstack ensures high-level security standards are always met and your code always runs in a secure cloud environment
  • Cross-platform testing: Browserstack also supports multiple operating systems, which enables testing across the device and browser combinations seamlessly
  • CI/CD Integration: It integrates smoothly with CI/CD pipelines for continuous testing, and tests can be part of deployment pipelines.
  • Budget-friendly: Maintaining in-house infrastructure is costly and challenging for organizations. Cloud infrastructure offers a more budget-friendly solution with advanced capabilities.

BrowserStack Automate Banner

Conclusion

Cypress is designed with a different architecture than traditional automation testing tools. Most test automation tools significantly challenge test suite performance. Cypress overcomes this with the Cypress session function.

Though Cypress provides an advanced option, it lacks parallel execution; however, this can be easily overcome by BrowserStack integration without modifying the code.

Try BrowserStack for Free

Frequently Asked Questions

What does cy.session do?

Cy.session() is a Cypress command that caches and restores the session across the specs. This command helps improve performance by eliminating repetitive logins.

What is a session example?

A session refers to the duration or period the user interacts with the application. It starts as soon as the user logs into the system and ends after the user logs out. Sessions are typically managed using cookies, tokens, or server-side components.

Tags
Automation Frameworks Automation Testing Cypress Real Device Cloud

Featured Articles

Using Cy.wrap in Cypress

Handling Test Failures in Cypress A Comprehensive Guide

Automation Tests on Real Devices & Browsers

Seamlessly Run Automation Tests on 3500+ real Devices & Browsers