2

This example is very close to what I need, except that the urls are not known ahead of time (can't hardcode them).

The urls are generated in the before() hook by running a script:

before(() => {
  cy.exec(<run script that generates a temporary urls.json file>);
  cy.readFile("./urls.json")....
});

How could I dynamically generate separate tests based on the generated urls.json?

The reason they need to be separate tests is this.

1 Answer 1

5

The test enumerator .forEach() runs before the before() hook, but that's ok if you know exactly how many urls you need to process.

In the Cypress example you cited, setting urls in before().

let urls = []; 

describe('Logo', () => {

  before(() => {
    cy.exec('npm run generator')
      .then(() => {  // writing the file will be async
        cy.readFile("./urls.json").then(data => {
          urls = data;
      });
    });
  })

  Cypress._.range(0, 2).forEach(index => {       // only need the number of tests here

    it(`Should display logo #${index}`, () => {
      const url = urls[index]                    // runs after before()
      cy.visit(url)
      ...
    })
  })
})

Note, url can't be in the test description any more, but index can.

If the number of urls is unknown, it is still technically possible by setting the test enumerator to a maximum, but the results in the log are messy. Unused test slots still show as passing.


The basic problem is the generator script needs to run in the Cypress Node process, but the spec runs in the browser process.

But ipc communication between browser and node is done via async cy.* commands that can only run in callbacks, which can only run during the execution phase.

You would be better off running the generator script externally, something like

"scripts": {
  "gen:test": "npm run generator & cypress open"
}

then using a simple require() to pick up the data

const data = require('./urls.json')
let urls = data.urls;
const testCount = data.urls.length;  

describe('Logo', () => {

  before(() => {
    // not required
  })

  Cypress._.range(0, testCount).forEach(index => {

  or

  urls.forEach((url, index) => {
Sign up to request clarification or add additional context in comments.

2 Comments

Really useful post. I also observed that '.forEach' works in this mechanism but not the regular 'for' loop.
Regarding the top code snippet.... I imagine there is no way (in the presumed scenario where URLs are not known beforehand, but an upper number considered), that we can somehow accomodate the 'URL' info in the test name (instead of just the # variable as postfix to "Should display logo")? I am wondering if we can have more informative test names when using the approach. My guess is no, as the info isn't available beforehand.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.