skip to Main Content

I have a data driven cypress code like below.
I’m trying to generate tests dynamically based on the values of "data" variable.

    let data:QuestionData[];
describe("Add data to admin", ()=>{
    before(async ()=>{
        data = await promisify(cy.task("readCSV",csvFile));
    });
    data.forEach((val,index)=>{
        it(`Should add val ${val}`, ()=>{
            console.log(index);
        })
    })

});

my "readCSV" method just reads a csv and resolve data.

when I run above code I’m getting this error -> Cannot read properties of undefined (reading ‘forEach’).

The reason is "data.forEach" runs before the variable gets initialized.
if I write a "console.log" inside the "before" hook I can see data is available.
But why data variable NOT getting available outside the BEFORE HOOK ?
My code works if I access "data" variable inside "it" block and use for lood.
but I want tests to be generated dynamically.

How can I fix this in cypress.io ?

2

Answers


  1. Try changing the arrow functions to normal ones, it can mess with the context: https://mochajs.org/#arrow-functions

    And doesn’t cy.task() return a Promise? Calling promisify() on a Promise should throw an error. And even then, promisify() doesn’t return a Promise, it returns a function that returns a Promise.

    Login or Signup to reply.
  2. The problem is Cypress is attempting to use the data before any commands have run (including cy.task() in the before() block). Cypress builds the test queue by running the javascript of the test file, then it runs the test queue.

    Cypress has the Before Run API to get around this.
    (Note set experimentalInteractiveRunEvents:true option in `cypress.config.js’ to use this feature).

    You can switch from this

    module.exports = defineConfig({
      e2e: {
        setupNodeEvents(on, config) {
          on('task', {
            readCSV(csvFile) {
              // code to read the CSV
            },
          })
    

    to this

    module.exports = defineConfig({
      e2e: {
        setupNodeEvents(on, config) {
          on('before:run', () => {
            // code to read the CSV
          })
    

    The first problem is how to pass the csvFile parameter. If you are setting it by enviroment variable, you can read it from config.env or hard-code if always the same file name.

    module.exports = defineConfig({
      e2e: {
        setupNodeEvents(on, config) {
          on('before:run', () => {
            const csvFile = config.env.csvFile;
            // code to read the CSV
          })
    

    The second problem is how to send the data to the test. The easiest would be to put it in another environment var.

    module.exports = defineConfig({
      e2e: {
        setupNodeEvents(on, config) {
          on('before:run', () => {
            const csvFile = config.env('csvFile')
            // code to read the CSV
            config.env('data', data)
          })
    

    Then the test reads it synchronously at the top of the test. Cypress.env() will run outside the it() or before() blocks.

    let data = Cypress.env('data')
    
    data.forEach((val, index) => {
      it(`Should add val ${val}`, () => {
        // testing each data item
      })
    })
    

    Using require

    Another approach to require or import the CSV as a string and parse it using plain javascript.

    Assuming lines of comma-separated values, somthing like

    const csvString = require('../fixtures/my-data.csv')
    
    const lines = csvString.split(csvString, 'n')
    const data = lines.map(line => line.split(','))
    
    data.forEach((val, index) => {
      it(`Should add val ${val}`, () => {
        // testing each data item
      })
    })
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search