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

Viewed 16574

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

 })
8 Answers

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)
  });

});

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.

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.

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')
}

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);
  })

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

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)
    });
})
Related