Cypress - Data Driven Testing
Cypress - Data-Driven Testing with Fixtures
Section titled “Cypress - Data-Driven Testing with Fixtures”Data-driven testing is a technique where test data is stored separately from the test scripts. This allows you to run the same test logic with multiple datasets, improving test coverage and maintainability. Cypress achieves data-driven testing primarily through its fixtures feature.
Fixtures are files used to store static test data. By default, Cypress looks for fixture files in the cypress/fixtures folder. These files can be in various formats, with JSON (.json) being the most common.
A typical cypress/fixtures folder structure might look like this:
cypress/ fixtures/ example.json userCredentials.json productData.json integration/ (or e2e/ for Cypress 10+) login_spec.js // other test filesFixture data is typically organized in key-value pairs, especially when using JSON.
Using cy.fixture()
Section titled “Using cy.fixture()”The cy.fixture() command is used to load data from a fixture file. It can then be used within your tests.
Syntax
Section titled “Syntax”cy.fixture(filePath)cy.fixture(filePath, encoding)cy.fixture(filePath, options)cy.fixture(filePath, encoding, options)Where:
filePath: Path to the fixture file (relative to thecypress/fixturesfolder). You can omit the file extension if it’s.json.encoding: Encoding type (e.g., ‘utf8’, ‘ascii’) used to read the file. Defaults to ‘utf8’.options: An object to modify default behavior. For example,timeoutcan change the default wait time for the fixture to load.
Implementation Example
Section titled “Implementation Example”Let’s create a scenario where we test a login form using data from a fixture file.
1. Create a Fixture File
Section titled “1. Create a Fixture File”Create a file named cypress/fixtures/userLogin.json with the following content:
{ "username": "standardUser", "password": "secret_sauce", "invalidUsername": "lockedOutUser", "invalidPassword": "wrong_password"}2. Write the Test Script
Section titled “2. Write the Test Script”In your test file (e.g., cypress/integration/login_ddt_spec.js or cypress/e2e/login_ddt_spec.cy.js):
describe('Login Functionality - Data-Driven', () => { // Load fixture data before tests run before(function () { cy.fixture('userLogin').then(function (data) { // 'this.data' now holds the content of userLogin.json // Using standard 'function' allows 'this' to refer to the Mocha context this.loginData = data; }); });
it('Should login successfully with valid credentials from fixture', function () { cy.visit('https://www.saucedemo.com/'); // Example login page
cy.get('[data-test="username"]').type(this.loginData.username); cy.get('[data-test="password"]').type(this.loginData.password); cy.get('[data-test="login-button"]').click();
cy.url().should('include', '/inventory.html'); });
it('Should display an error with invalid credentials from fixture', function () { cy.visit('https://www.saucedemo.com/');
cy.get('[data-test="username"]').type(this.loginData.invalidUsername); cy.get('[data-test="password"]').type(this.loginData.invalidPassword); cy.get('[data-test="login-button"]').click();
cy.get('[data-test="error"]').should('be.visible') .and('contain.text', 'Username and password do not match'); // Adjust error message as per actual application });});In this example:
- The
beforehook loads theuserLogin.jsonfixture once before any tests in thedescribeblock run. cy.fixture('userLogin').then(function (data) { ... })makes the fixture data available asthis.loginDatawithin the test cases (itblocks). Using a standardfunction()for the callback is important here so thatthisrefers to the Mocha test context.- The tests then use
this.loginData.username,this.loginData.password, etc., to populate form fields.
Iterating Over an Array of Data
Section titled “Iterating Over an Array of Data”If your fixture contains an array of test data sets, you can iterate over them:
Fixture cypress/fixtures/multipleUsers.json:
[ { "username": "user1", "password": "pass1" }, { "username": "user2", "password": "pass2" }]Test script:
describe('Multiple User Login', () => { before(() => { cy.fixture('multipleUsers').as('users'); // Alias the fixture data });
it('should attempt login for each user', function() { // 'this.users' is now available due to the alias in the before hook this.users.forEach((userData) => { cy.log(`Attempting login for ${userData.username}`); cy.visit('https://www.saucedemo.com/'); cy.get('[data-test="username"]').type(userData.username); cy.get('[data-test="password"]').type(userData.password); cy.get('[data-test="login-button"]').click(); // Add appropriate assertions based on expected outcome for each user // For simplicity, this example only logs and attempts login. }); });});Using cy.fixture(...).as('aliasName') in a before or beforeEach hook makes the data accessible via this.aliasName in subsequent hooks and tests within the same scope.
Benefits of Using Fixtures
Section titled “Benefits of Using Fixtures”- Separation of Concerns: Keeps test data separate from test logic.
- Reusability: The same fixture data can be used across multiple tests.
- Readability: Makes tests cleaner and easier to understand.
- Maintainability: Easier to update test data without modifying test scripts.
While fixtures are excellent for static data, for dynamic data generation or more complex data scenarios, you might consider using JavaScript/TypeScript modules or custom Cypress commands. However, for most common data-driven testing needs, fixtures provide an elegant and straightforward solution.