What is the best practice of pass states between tests in Cypress
Asked Answered
D

10

20

I want to pass/share data between each test. What is the best way to implement it in Cypress?

For example:

 it('test 1'), () => {
   cy.wrap('one').as('a')
   const state1 = 'stat1'
 })

 it('test 2'), () => {
   cy.wrap('two').as('b')
 })

 it('test 2'), () => {
   //I want to access this.a and this.b

   //Also I want to access state1

 })
Decca answered 28/8, 2018 at 5:25 Comment(6)
Tests shouldn't share state - what's the context, why do you think you need this?Incense
Avoid using shorthand arrow functions if you want to use this.a within. Use function() { instead. @Incense sometimes you do need it: for example 1st test retrieves something from clipboard when something is clicked, 2nd one uses this specific value to test if certain request works.Lists
@Lists then your tests depend on one another and the order in which they're run, which is an anti-pattern.Incense
@Incense agreed, but how do you write a test like this without mocking everything or having 1 huge test? (running it as an integration/acceptance tests in CI after deployment)Lists
@Lists it's hard to say without more context, but from what you've described above: just make it one test.Incense
@Incense I was afraid you ll say that. Okay, will rethink a bit, tnxLists
L
8

In the case of Javascript variables, you can do something like this:

let state;

describe('test 1', () => {
    it('changes state', () => {
        state = "hi";
     });
});

describe('test 2', () => {
    it('reports state', () => {
        cy.log(state); // logs "hi" to the Cypress log panel
     });
});

.as() does not appear to be able to carry state between describe blocks.

Lonnalonnard answered 28/8, 2018 at 13:19 Comment(3)
A better method is by using the current suite context: set this.state and you can skip having to "let" (declare) the variable in the global scope. Note that you must then use function(){} in place of ()=>{} so that this gets assigned.Myron
This doesn't work if it tests navigate to different domains.Annelleannemarie
I had to do this when I needed data from a before() to be used in later tests.Riccardo
I
13

I can see the answer worked for author, but in case anyone needs to share data between different test files, the solution is to use cy task method and store data in Node environment, e.g. in my case I needed to store user data:

// cypress/plugins/index.ts
export default (on, config) => {
  on('task', {
    setUserData: (userData: UserDataType) => {
      global.userData = userData;
      return null;
    },
    getUserData: () => {
      return global.userData;
    },
  });
};

then in test case we can do:

// cypress/integration/login.spec.ts
describe('Login', () => {
  it('should work', () => {
    cy.visit('/login-page');
    cy.intercept('api/login-endpoint').as('postLogin');
    // login interactions
    cy.wait('@postLogin').then((interception) => {
      // intercept user data and store it in global variable
      cy.task('setUserData', JSON.parse(interception.response.body));
    });
    // ... further assertions
  });
});

later we can retrieve this data easily:

// cypress/integration/otherTest.spec.ts
describe('Other test', () => {
  it('uses user data', () => {
    cy.task('getUserData').then((userData: UserDataType) => {
      console.log(userData);
      // voila! Stored data between two .spec files
    });
  });
});

You'll also need to extend Node TS types for this, but this answer is long enough already.

Yes, I know it is not a great way of writing tests at all, as it makes them depend on each other, but sometimes a long interaction flow in application makes it necessary.

Iodous answered 17/11, 2021 at 13:38 Comment(0)
K
10

As abbr pointed out in the comments, sharing a value using a global variable will not work if the tests start on a different url.

So these do not work:

  • global variable assign and read (does not matter if it is outside the describe / context block or not)
  • relying on Mocha this.state (this gets cleared after the test)
  • using Cypress as

What remains of course is file. Read and write.


describe("Test", () => {

  it("Can access 3000", function () {
    cy.visit("http://localhost:3000");

    cy.writeFile("shared.json", {count: 2})
  });


  it("Can access 8000", function () {
    cy.visit("http://localhost:8000");

    cy.readFile("shared.json").then(cy.log)
  });

});

Konyn answered 27/5, 2021 at 12:38 Comment(1)
Please note that writeFile() does not work with ts spec files.Alexio
L
8

In the case of Javascript variables, you can do something like this:

let state;

describe('test 1', () => {
    it('changes state', () => {
        state = "hi";
     });
});

describe('test 2', () => {
    it('reports state', () => {
        cy.log(state); // logs "hi" to the Cypress log panel
     });
});

.as() does not appear to be able to carry state between describe blocks.

Lonnalonnard answered 28/8, 2018 at 13:19 Comment(3)
A better method is by using the current suite context: set this.state and you can skip having to "let" (declare) the variable in the global scope. Note that you must then use function(){} in place of ()=>{} so that this gets assigned.Myron
This doesn't work if it tests navigate to different domains.Annelleannemarie
I had to do this when I needed data from a before() to be used in later tests.Riccardo
D
1

Assuming you are trying to pass text

it('test 1'), () => {
  cy.wrap('one').as('a')
}

it('test 2'), () => {
  cy.wrap({ valueName: 'two' }).as('b')
}

it('test 2'), () => {
  //I want to access this.a and this.b
  cy.get('@a').then((thisIsA) => {
    cy.log(thisIsA);
    // logs 'one'
  }

  cy.get('@b').its('valueName').then((thisIsB) => {
    cy.log(thisIsB);
    // logs 'two'
  }

  cy.get('@b').its('valueName').should('eq', 'two')
}
Dhammapada answered 6/9, 2018 at 23:50 Comment(1)
Not working in cypress 3.6.1 github.com/cypress-io/cypress/issues/1392Foetor
P
1

While working on this problem, I accidentally found solution. Not sure why this works. Import any json file (this solution will not change json file)

someTest.spec.js:

import jsonFile from "../../fixtures/file.json"
describe('Test some feature', () =>{
      it('test feature 1', () => {
        jsonFile.anyWalueYouWant="this is value from feature 1"                          
      })

      it("test feature 2", ()=>{
        cy.log(jsonFile.anyWalueYouWant)
    })

})

PS: anyWalueYouWant dont need to be in this parameters.json file

Plague answered 28/12, 2021 at 17:43 Comment(0)
L
1

Another option is the use of session cookies

https://docs.cypress.io/guides/core-concepts/conditional-testing#Element-existence

Although I don't really like this, a further option, similar to the tasks is to use custom commands

let state = {};
Cypress.Commands.add("setState", (name, value) => {
  state[name] = value;
  return value;
});

Cypress.Commands.add("getState", () => {
  return state;
});

Usage is then nice and pretty ;)

cy.setState("item", "value");
cy.getState().then((state) => {
  cy.log(state.item); // "value"
});
Lathrop answered 10/9, 2022 at 21:38 Comment(0)
H
0

I tried some of these other solutions and they aren't working for me. Maybe things changed with recent versions. The following example should work. Note the use of function() for both tests which keeps things in context of 'this.'.

  it('get the value', function () {
    cy.get('#MyElement').invoke('text').as('mytext1')
  })

  it('use the value', function () {
    cy.log(this.mytext1);
  })
Hearne answered 14/10, 2020 at 18:31 Comment(1)
This is undefined for mePinnatiped
D
0

You can write the data to JSON, then access it later with cy.fixture(), or cy.readFile().

See https://docs.cypress.io/api/commands/writefile#Write-response-data-to-a-fixture-file

Disputatious answered 29/11, 2023 at 20:55 Comment(0)
V
0

Created plugin, it will do exactly what you need get-data-from-cache

V2 answered 19/7, 2024 at 7:50 Comment(1)
this is essentially a link-only comment. You can improve your answer by explaining what the is behind the link.Alice
Q
-2

You can share state between steps like this

describe('A suite of steps', () => {

    it('visit a page and save the url', function () {
        cy.visit('/hello-world')
        // Store url
        cy.url().as('pageUrl');
    });

    it('load page url', function () {
        // Load the page from the previous step
        cy.visit(this.pageUrl)
    });
})
Quattrocento answered 13/8, 2021 at 23:57 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.