Using JSON Files for Managing Test Data in Playwright
Recently, I joined a new product team where we are developing UI validation tests with Playwright and TypeScript. Previously, I had mostly worked with Playwright using Cucumber.js as a test runner. However, this time we decided to use the Playwright test runner. Over the past few months, I’ve learned a lot and will be sharing many insights here.
During one such scenario, I was required to automate a huge set of dropdowns with similar components. Consider a page that has let’s say ten grids, where each grid has three different dropdowns. These dropdowns are common across individual grids.
At first, I wrote the test something like this:
const testData = [
{
criteria: 'City',
comparator: 'equal to',
value: 'Pune'
},
{
criteria: 'State',
comparator: 'equal to',
value: 'Maharashtra'
},
{
criteria: 'Pin',
comparator: 'greater than',
value: '4211'
},
{
criteria: 'Country',
comparator: 'contains',
value: 'Ind'
},
{
criteria: 'Continent',
comparator: 'equal to',
value: 'Asia'
},
..........
]
for(data of testData) {
await this.locator['Add New Button'].click();
await this.selectCriteria(data.criteria);
await this.selectComparator(data.comparator);
await this.selectValue(data.value);
}
This code looks well optimized as we are not repeating the steps to first click on the ‘Add New’ button and then select values from each dropdown set. And then repeating the same stuff for every set. Instead, an array object is created to define the data, and then a for loop is utilized to iterate through each object and perform the selection of dropdown values.
Optimizing Test Data Management
However, this encapsulation of test data and the logic looks a bit lengthy. Then, I thought of moving the test data to some other location, somewhere like test-data
. Initially, I wanted to keep most of the relevant test data in a .ts
file and export them. However, I learned later that it is a good practice to store array objects in a .json
file instead.
So, we can store the test data array object in a .json
file something like this:
[
{
"criteria": "City",
"comparator": "equal to",
"value": "Pune"
},
{
"criteria": "State",
"comparator": "equal to",
"value": "Maharashtra"
},
{
"criteria": "Pin",
"comparator": "greater than",
"value": "4211"
},
{
"criteria": "Country",
"comparator": "contains",
"value": "Ind"
},
{
"criteria": "Continent",
"comparator": "equal to",
"value": "Asia"
}
]
Now, before importing this JSON, open your tsconfig.json
file and ensure that the resolveJsonModule
and esModuleInterop
options are enabled. It should look something like this:
{
"compilerOptions": {
"resolveJsonModule": true,
"esModuleInterop": true,
// other options...
}
}
Now, this is how the method in a page object class will look like:
// page-object class
async selectDropdownValues(data: {
criteria: string;
comparator: string;
value: string;
}[])
for(data of testData) {
await this.locator['Add New Button'].click();
await this.selectCriteria(data.criteria);
await this.selectComparator(data.comparator);
await this.selectValue(data.value);
}
This method can be invoked in a spect.ts
file by passing an array object as an argument. We need to import a JSON file which can be used as an argument to selectDropdownValues()
.
// spec.ts file
import data '../test-data/dropdown.json';
test('some test', async() => {
await pageObjectInstance.selectDropdownValues(data);
})
Conclusion
This way, we can define our test data in a more structured manner in a JSON file and optimize the function body smartly. This approach enhances code maintainability and readability, especially when dealing with large sets of test data.
Shiv Jirwankar
SDET