Import a JSON fixture file to create dynamic Cypress tests
Often, you need to run the same test with different data. For example, one might want to test how the backend API handles creating an item for multiple items with different parameters. We could write a separate test for each item.
The separate tests have a lot of repetitive code, and we are not even checking if the item is marked completed on the page! We could the items in the JSON file to be loaded and used in a single test.
it('in a single test', () => { cy.fixture('three.json') .its('data.allTodos') .then((list) => { list.forEach((item) => { // clear all existing items deleteAll()
// create the item using a network call cy.request({ method: 'POST', url: 'http://localhost:3000/', body: { operationName: 'AddTodo', query: ` mutation AddTodo($title: String!, $completed: Boolean!) { createTodo(title: $title, completed: $completed) { id } } `, variables: { title: item.title, completed: item.completed, }, }, }) // visit the page and check the item is present cy.visit('/') const classAssertion = item.completed ? 'have.class' : 'not.have.class' cy.contains('.todo', item.title).should(classAssertion, 'completed') }) }) })
The above test is a little cumbersome to read, and if a single request goes wrong, the test stops without trying to testing other items from the list. We would like to have a separate test for each item instead.
Separate tests
cypress/integration/dynamic-spec.js
1 2 3 4 5 6 7 8 9 10 11 12
describe('Creates each item', () => { let items // DOES NOT WORK, CANNOT ADD NEW TESTS // AFTER THE RUN HAS STARTED before(() => { cy.fixture('three.json').then((data) => { items = data.data.allTodos }) })
// hmm, how do we create a test for each item? })
☢️ If we load the list dynamically using cy.fixture or any other Cypress command, then the tests are already running, and it is too late to add new tests. Thus we need to load the fixture before any tests execute.
We can import the JSON file instead of using cy.fixture - that way the bundler will load the JSON file into the spec and the loaded data will be available to define tests.
// import the fixture with the data for new tests import { data } from'../fixtures/three.json' import { deleteAll } from'./utils'
describe('Creates each item', () => { beforeEach(deleteAll)
// create a test for each item imported from the fixture data.allTodos.forEach((item, k) => { it(`creates item ${k + 1}`, () => { // create the item using a network call cy.request({ method: 'POST', url: 'http://localhost:3000/', body: { operationName: 'AddTodo', query: ` mutation AddTodo($title: String!, $completed: Boolean!) { createTodo(title: $title, completed: $completed) { id } } `, variables: { title: item.title, completed: item.completed, }, }, }) // visit the page and check the item is present cy.visit('/') const classAssertion = item.completed ? 'have.class' : 'not.have.class' cy.contains('.todo', item.title).should(classAssertion, 'completed') }) }) })
The above approach creates a separate test for each item imported from the fixture. It is equivalent to having the items as a "static" list present in the spec file itself: