---
title: "A common mistake when using cy.session() (and how to fix it)"
url: "https://www.cypress.io/blog/mistake-when-using-cy-session-and-how-to-solve-it"
description: "This is a guest post from Ambassador Filip Hric!

Hello, I’m Filip. I teach testers about web development and developers about testing. I have spoken at conferences around the world, created multiple online courses, and host live workshops on e2e testing in Cypress.io. Find everything I do at https://filiphric.com.

In Cypress v12, the cy.session() command was released as generally available. Many teams have already added it to their projects to save minutes from their test runs.

I recently wro"
---

[Blog](/blog/)

# A common mistake when using cy.session() (and how to fix it)

February 8, 2023

•

By Guest

> _**This is a guest post from Ambassador Filip Hric!**_  
>   
> _Hello, I’m Filip. I teach testers about web development and developers about testing. I have spoken at conferences around the world, created multiple online courses, and host live workshops on e2e testing in Cypress.io. Find everything I do at https://filiphric.com._

* * *

In Cypress v12, the [`cy.session()`](https://docs.cypress.io/api/commands/session?ref=cypress-io.ghost.io) command was released as generally available. Many teams have already added it to their projects to save minutes from their test runs.

[I recently wrote a blogpost](https://filiphric.com/use-session-instead-of-login-page-object-in-cypress?ref=cypress-io.ghost.io) on how `cy.session()` can be used instead of abstracting login logic into a page object. As community members on my [Discord](https://filiphric.com/discord?ref=cypress-io.ghost.io) implement `cy.session()` into their projects, I often see one problem pop up repeatedly. Let me give you an example.

Consider this login flow:

```js
cy.visit('/login')

cy.get('[data-cy="login-email"]')
   .type('filip@example.com')

cy.get('[data-cy="login-password"]')
   .type('Asdf.1234#{enter}', { log: false })
```

By applying the `cy.session()` API, we can cache the cookies, `localStorage` and `sessionStorage` state of our browser for this login flow like this:

```js
cy.session('login', () => {

   cy.visit('/login')

   cy.get('[data-cy="login-email"]')
      .type('filip@example.com')

   cy.get('[data-cy="login-password"]')
      .type('Asdf.1234#{enter}', { log: false})
})
```

There is one small problem with this type of login. As soon as Cypress completes the `cy.type()` function, it caches our browser state. However, our frontend might still be working on the login flow and some of the authentication data we need has not been set.

Let’s say our application login will send an http request to the server to authenticate the user, and then redirect to a home page, before saving cookies into the browser.

Given the example above, our session will not get properly cached because we didn’t not explicitly tell `cy.session()` authentication flow is still in progress. The easiest way we can fix that is to add a guarding assertion to our session.

```js
cy.session('login', () => {

   cy.visit('/login')

   cy.get('[data-cy="login-email"]')
      .type('filip@example.com')

   cy.get('[data-cy="login-password"]')
      .type('Asdf.1234#{enter}', { log: false })

   cy.location('pathname')
      .should('eq', '/')
})
```

This way we will ensure that Cypress will cache our browser at the right moment. Depending on our application, this might be right after we navigate to a new page or some time later. The exact point will be determined by the application we are testing.

Usually, when making a login action, a server responds with an authorization in the form of a token that gets saved as cookies or some other browser storage. Instead of checking for the location, we can check that storage item. The session might then look something like this:

```js
cy.session('login', () => {

   cy.visit('/login')

   cy.get('[data-cy="login-email"]')
      .type('filip@example.com')

   cy.get('[data-cy="login-password"]')
      .type('Asdf.1234#{enter}')

   cy.getCookie('auth_token')
      .should('exist')
})
```

There is a slight problem with this approach though. The `cy.getCookie()` command will execute immediately and will not retry. Instead of using `cy.getCookie()` command we will choose a slightly different approach.

```js
beforeEach(() => {

   cy.session('login', () => {

      cy.visit('/login')

     cy.get('[data-cy="login-email"]')
        .type('filip@example.com')

     cy.get('[data-cy="login-password"]')
        .type('Asdf.1234#{enter}')

     cy.document()
        .its('cookie')
        .should('contain', 'auth_token')
   })
});
```

This way we can ensure that we wait the proper time for the application to store our cookies in the browser. After our cookies are saved, we can safely cache our session and use it in our tests.

The validation of a successful login is important for the `cy.session()` command to work properly. In fact, when using `cy.session()` you can add the validation as a separate step. This is what the `validate()` function is for. Let’s see it in action:

```js
beforeEach(() => {

  cy.session('login', () => {

    cy.visit('/login')

    cy.get('[data-cy="login-email"]')
      .type('filip@example.com')

    cy.get('[data-cy="login-password"]')
      .type('Asdf.1234#{enter}')
}, {
    validate() {
      cy.document()
        .its('cookie')
        .should('contain', 'auth_token')
    }
  })
});
```

In this simple example, `cy.session()` will work pretty much the same way as before. But there is one upside to this approach. Our `validate()` function can do more than check the mere presence of the token. Instead, we could check for the validity of the token and make sure it does not expire during, e.g., a longer test run. The validate function can look something like this:

```js
validate() {
      cy.document()
        .its('cookie')
        .should('contain', 'trello_token')
        .then(token => {
          cy.request({
            url: '/api/users',
            headers: {
              authorization: token
            }
          })
        })
    }
```

If the `cy.request()` returns an error code, it will mean that our authorization is no longer valid and we need to log in again. The `validate()` function will take care of this, and re-run our login flow.

This way we still keep login attempts to minimum, while making sure that the authorization does not expire during the test run.

Remember to always ensure that your `cy.session()` flow contains a “confirming assertion” that will guard the creation of the session to proper time.

* * *

**_About the Ambassador Program_**

The Cypress Ambassador program supports the top Cypress advocates around the world. Through this program, Ambassadors are offered speaking opportunities, a personalized hub, and visibility within our extensive network.To learn more about these wonderful ambassadors visit our [Official Ambassador webpage.](https://www.cypress.io/ambassadors/?ref=cypress-io.ghost.io)