Sometimes when you’re writing a test you’ll find you want to run the same test multiple times, but with different data.
For example let’s say you need tests for a UK e-commerce website to verify that the correct sales tax (VAT) is applied to purchases. Most products in the UK incur VAT of 20% but there are exceptions:
- Books = 0%
- Children’s clothing = 0%
- Toys = 0%
- Solar panels = 5%
These could be written as 4 separate tests but since the steps will be identical it’s easier and more maintainable to write as a single, parameterized Playwright automated test.
To do this create an array of the data parameters, and then write a looping mechanism enveloping the test, iterating through each of the data sets.
// define your Playwright test data in an array of parameters
const productData = [
{ name: 'knowledge of angels book', tax: '0' },
{ name: 'Nike Boys T-Shirt', tax: '0' },
{ name: 'LEGO Triceratops', tax: '0' },
{ name: 'Jackery Solar 100W', tax: '5' },
]
for (const product of productData) {
test(`Sales tax test for ${product.name}`, async ({page}) => {
await page.goto('https://www.example.com');
// locate product
await page.locator('#searchField').fill(product.name);
await page.locator('#searchButton').click();
// add to basket
await page.locator('#product1').click();
await page.locator('#addToBasket').click();
// verify correct sales tax applied
await page.locator('#goToBasket').click();
await expect(page.locator('#tax1')).toHaveText(product.tax);
});
}
When you execute this you will see Playwright running 4 tests.
Also note the execution log will show a different test name for each test because we added the product name in the test description:
test(`Sales tax test for ${product.name}`, async ({page}) => {
Gotcha #1 – Error: duplicate test title
This common error occurs because the test title needs to be unique for all iterations of the test.
In the example below, the test title is the same `Sales tax test` for all 4 iterations of the productData loop, so is incorrect. We need to include a value from the loop to make each test title unique, as we did in the original example (`Sales tax test for ${product.name}`).
// define your Playwright test data in an array of parameters
const productData = [
{ name: 'knowledge of angels book', tax: '0' },
{ name: 'Nike Boys T-Shirt', tax: '0' },
{ name: 'LEGO Triceratops', tax: '0' },
{ name: 'Jackery Solar 100W', tax: '5' },
]
for (const product of productData) {
test(`Sales tax test`, async ({page}) => {
await page.goto('https://www.example.com');
etc...
});
}
Gotcha #2 – Using ‘var’ for loop variables
Another common error occurs when we use this loop construct to execute multiple tests, where var is used to declare the variable i:
for(var i = 0; i < 5; i++) {
test(`Test number ${i}`, async ({ page }) => {
console.log(`executing test number ${i}`);
});
}
When you run this test you’ll see something unexpected:
Running 5 tests using 1 worker
[chromium] › tests\change\change-type.spec.ts:171:7 › Test number 0
executing test number 5
[chromium] › tests\change\change-type.spec.ts:171:7 › Test number 1
executing test number 5
[chromium] › tests\change\change-type.spec.ts:171:7 › Test number 2
executing test number 5
[chromium] › tests\change\change-type.spec.ts:171:7 › Test number 3
executing test number 5
[chromium] › tests\change\change-type.spec.ts:171:7 › Test number 4
executing test number 5
5 passed (1.9s)
Although the test title is displayed correctly – “Test number 0”, “Test number 1” and so on – the console.log statements inside the test always display a value of “5” for the iterator variable i.
The reason for this is that var for variable declarations is function scoped, not block scoped. By the time the test runs (which is asynchronous and deferred), the loop has completed, and i is now 5. All your tests are referencing the same i variable, which has been updated.
To fix this use let instead of var:
for(let i = 0; i < 5; i++) {
test(`Test number ${i}`, async ({ page }) => {
console.log(`executing test number ${i}`);
});
}
Which should now produce the correct output:
Running 5 tests using 1 worker
[chromium] › tests\change\change-type.spec.ts:171:7 › Test number 0
executing test number 0
[chromium] › tests\change\change-type.spec.ts:171:7 › Test number 1
executing test number 1
[chromium] › tests\change\change-type.spec.ts:171:7 › Test number 2
executing test number 2
[chromium] › tests\change\change-type.spec.ts:171:7 › Test number 3
executing test number 3
[chromium] › tests\change\change-type.spec.ts:171:7 › Test number 4
executing test number 4
5 passed (2.1s)
I hope this simple guide to parameterizing Playwright tests has been useful!