While designing a Test Automation Framework, one of the primary requirements is to segregate the test scripts from the test data. Additionally, if the same framework can provide the functionality to mock the backend/third-party requests and offer the capability to test the frontend independently, it will be the icing on the cake. Cypress provides both of these functionalities, which we can quickly achieve with the help of Fixtures in Cypress. Let's understand these capabilities of Cypress by understanding the following topics in Cypress:
- What is a fixture in Cypress?
- How to implement fixtures as a test data source in Cypress?
- Procedure to run the same test with different fixtures?
What is a fixture in Cypress?
As already stated above, Cypress provides us with a feature to drive the data from external sources. We can draw a comparison to the Data-Driven Frameworks, which are one of the popular automation testing framework types in the current market. As we all know that, in Data-Driven Frameworks, we generally keep the data in excel files and use external libraries such as Apache POI to read the data and use it in our test with an automation framework. Similarly, Cypress has provided the same functionality in its architecture, where we can read the data from external files known as fixture files.
Now, as we discussed in the article "Cypress Tests," Cypress provides a directory named as fixtures, which stores various "JSON" files, and these JSON files can store the test data which can be read by multiple tests. We store test data in the form of key-values, which we can access in the test scripts.
How to implement fixtures as a test data source in Cypress?
As we discussed, fixtures can store and serve test data in Cypress tests. The implementation of fixtures is a two-step process:
- Defining a fixture file, which will store the test data.
- Using the fixture file in test scripts and access the test data.
Let's understand how can we implement both of these steps:
How to define a fixture in Cypress?
To understand the concepts and implementation of fixtures in detail, we will use the demo website. Suppose we want to automate the following test case:
- Open the URL, which is My Account page of E-commerce demo website - http://shop.demoqa.com/my-account/.
- Then do the registration using a valid username, email, and password.
- Verify if the registration was successful or not.
Now to accomplish these tasks, we need a valid username, email, and password. One way to pass this data is directly in the test script, but that will hard code the data set. To avoid this hard-coding problem, we can specify this test data in the fixture files. When we created the project hierarchy for Cypress, it automatically creates a folder named "fixtures" and a file named "example.json" under the fixtures folder. We can use this file to specify our test data and edit it to put the following fundamental values:
{
"Username": "aashishk7",
"Email":"aashish.khetarpal.17@gmail.com",
"Password": "cypresstutorials",
"NewPassword":"cypresstutorials77"
}
After saving the test data in the fixture file, it will look in the project hierarchy, as shown below:
So, as a first step, we defined a fixture file named "example.json", which will store a set of usernames, email, and password for our test case. Now, let's understand how to use this test data in our test scripts.
How to use the fixture in test scripts?
As a next step, we have to consume this data in our test scripts. For this, Cypress gives us a method "fixture()", which can invoke on the cy object. Its syntax looks like below:
cy.fixture(filePath)
cy.fixture(filePath, encoding)
cy.fixture(filePath, options)
cy.fixture(filePath, encoding, options)
Where,
filePath (String) is a path to a file that you have written within the fixtures folder(defaults to cypress/fixtures).
encoding (String) is put to use when reading the file. Some of the popular encodings supported are ascii, utf-8, utf-16le, hex, etc.
options (Object) changes the response timeout.
Option | Default | Description |
---|---|---|
timeout | responseTimeout | Time to wait for cy.fixture() to resolve before timing out |
Now considering the automating the above scenario of registration of the "http://shop.demoqa.com/", we can write out test script as shown below:
// type definitions for Cypress object "cy"
// <reference types="cypress" />
describe('Automation Test Suite - Fixtures', function () {
//Use the cy.fixture() method to pull data from fixture file
before(function () {
cy.fixture('example').then(function (data) {
this.data = data;
})
})
it('Cypress Test Case - Understanding Fixtures', function () {
//Provide the data read from the fixture
cy.visit('https://shop.demoqa.com/my-account/');
cy.get('#reg_username').type(this.data.Username);
cy.get('#reg_email').type(this.data.Email);
cy.get('#reg_password').type(this.data.Password)
//Checking whether the Register button is disabled or not either by grabbing the property or by checking its UI behavior
cy.get('.woocommerce-Button').should('have.attr', 'disabled', 'disabled');
cy.get('.woocommerce-Button').should('be.disabled');
// Clicking on to register on the Website by entering new password
cy.get('#reg_password').type(this.data.NewPassword);
cy.get('.woocommerce-Button').click();
//Checking whether the Registration is successful and whether UserName is populated under login section
cy.get('#username').should('have.value', this.data.Username);
})
})
As we can see from the above code snippet, we used the cy.fixture() method in the before() method to copy all the data from the fixture file "example.json" to the local object "this". Also note that we use the name of the fixture file with-out file extension, i.e., only "example" is being used instead of "example.json".
Save the above test files as "CypressTest7.js" and run the test as per steps mentioned in the article "Cypress- Test Runner". It will show the sample output, as shown below:
As is evident from the above screenshot, the test successfully read the data from the fixture file and used the same while running the test case.
Procedure to run the same test with different fixtures?
So now, there can be a situation that you want to run your test with multiple sets of data, and you have that data saved in different fixtures file - F1 and F2, for example. Now you have to run your test with both the fixtures, so what will you do? One of the strategies could be that you copy your test code and create multiple "it" blocks or "describe" blocks and load different fixture files in each of them and run your tests. But by following this approach, you will be duplicating your code, which is not advisable while writing your tests in any programming language. So let's see another way how we can do this.
We will take the same example as above and will rename the existing fixture file to 'onefixture.json' and will create another feature file under the same folder 'twofixture.json'. So the data we will be using here will be different in each fixture file. In the first fixture, we are using the existing one, which is :
{
"Username": "aashishk7",
"Email":"aashish.khetarpal.07@gmail.com",
"Password": "cypresstutorials",
"NewPassword":"cypresstutorials77"
}
In the second fixture file, we will change the username and email address so that we can verify them when we run our test, so below will the second fixture file :
{
"Username": "aashishk97",
"Email":"aashish.khetarpal.907@gmail.com",
"Password": "cypresstutorials",
"NewPassword":"cypresstutorials77"
}
Now we will refactor the same test and will include a variable that will save the fixture name and a context variable pointing to each fixture file. So below are the changes done to accommodate that :
// type definitions for Cypress object "cy"
// <reference types="cypress" />
const availablefixtures = [
{
"name": "onefixture",
"context": "1"
},
{
"name": "twofixture",
"context": "2"
}
]
describe('Automation Test Suite - Fixtures', function() {
//loop through both the fixtues
availablefixtures.forEach((afixture) => {
describe(afixture.context, () => {
//Mostly used for Setup Part
before(function(){
cy.fixture(afixture.name).then(function(data)
{
this.data=data ;
})
})
//Calling
cy.visit('https://shop.demoqa.com/my-account/');
cy.get('#reg_username').type(this.data.Username);
cy.get('#reg_email').type(this.data.Email);
cy.get('#reg_password').type(this.data.Password)
//Checking whether the Register button is disabled or not either by grabbing the property or by checking its UI behavior
cy.get('.woocommerce-Button').should('have.attr','disabled','disabled');
cy.get('.woocommerce-Button').should('be.disabled');
// Clicking on to register on the Website by entering new password
cy.get('#reg_password').type(this.data.NewPassword);
cy.get('.woocommerce-Button').click();
//Checking whether the Registration is successful and whether UserName is populated under login section
cy.get('#username').should('have.value',this.data.Username);
})
})
})
})
The significant changes that we have done are :
- Included the 'availablefixtures' variable and saved the fixtures information that we want to use along with the context variable, which points to these fixtures.
- Then we have used a for-each loop to traverse through every object saved in that variable. Also, we have included the describe block. It ensures that every time it runs, a different feature goes for testing.
- The rest of the code remains the same. We are just verifying the login functionality by reading the data from the fixtures file.
Now let's run the test and see if correct data served to every test that we targeted for :
As we can see in the above runs, the first test is picking the data that we have saved in 'onefixture.json' as highlighted with marker 1. Similarly, the second test is picking the data that we have saved in 'twofixture.json' as highlighted with marker 2. So this way, we can read multiple fixtures within the same test.
So, it makes it clear that fixtures make it very easy to enable data-driven automation in Cypress. Moreover, the same test can execute against various data sets saved in the fixture files.
Key Takeaways
- Fixtures enable data-driven automation in Cypress by saving the test data in JSON format
- Cypress allows the same test script to run against multiple fixtures files, which ensures code reusability.
Let's move to the next article, where we will understand "How to define custom commands in Cypress?"
If you want to learn more you can look at this video series: ++Cypress video series++