Skip to content
Maximizing Testing Efficiency: Parameterizing Playwright Scripts

Maximizing Testing Efficiency: Parameterizing Playwright Scripts

Parameterization in testing is a powerful technique that allows you to run the same test script with different inputs, configurations, or scenarios. In the world of browser automation and testing, Playwright provides various methods to parameterize your scripts, making it easier to validate different use cases and ensure the robustness of your web applications.

In this blog post, we'll explore several approaches to parameterizing Playwright scripts.

Elevate your Load Testing!
Request a Demo

Why Parameterize Playwright Scripts?

Parameterizing your Playwright scripts offers several benefits, including:

  1. Increased Test Coverage: By running the same script with various parameters, you can verify different test scenarios, ensuring comprehensive coverage of your application.

  2. Reduced Code Duplication: Parameterization helps you avoid writing redundant code for similar test cases, making your test suite more maintainable.

  3. Efficient Testing: Parameterization allows you to test multiple data sets, configurations, and inputs without having to write separate test scripts for each case.

  4. Improved Maintainability: Changes in test data or configurations are easier to manage when the script is parameterized.

Prerequisites

Before diving into Playwright tests parameterizing, ensure you have the following prerequisites in place:

  1. Playwright: You should have Playwright installed. Playwright Trace Viewer is included as part of the Playwright package, so you don't need to install it separately. Please refer to our previous blog post on Getting Started with Playwright to install the Playwright testing environment.
  2. A Playwright Automation Script: You'll need a Playwright script that you want to parameterize. If you don't have one, you can create the file tests/petstore.spec.ts and use the following script for its content.
import {test, expect} from '@playwright/test';

test('test', async ({page}) => {
    await page.goto('https://petstore.octoperf.com/');
    await page.getByRole('link', {name: 'Enter the Store'}).click();
    await page.getByRole('link', {name: 'Sign In'}).click();
    await page.locator('input[name="username"]').click();
    await page.locator('input[name="username"]').fill('user1');
    await page.locator('input[name="password"]').click();
    await page.locator('input[name="password"]').fill('pass');
    await page.getByRole('button', {name: 'Login'}).click();
    await page.locator('#SidebarContent').getByRole('link').first().click();
    await page.getByRole('link', {name: 'FI-SW-01'}).click();
    await page.getByRole('link', {name: 'EST-1'}).click();
    await page.getByRole('link', {name: 'Add to Cart'}).click();
});

In this script we simulate a user that browses a fictitious e-commerce web application JPetstore.

We will start by parameterizing login and password used to connect to the website. Then we will se how to loop over random items and add them to the buyer's cart.

Methods to Parameterize Playwright Scripts

Parameterizing Playwright tests is a valuable practice that allows you to run the same test scenario with different input data or configurations. This helps ensure that your application behaves consistently across various test cases.

Now, let's explore different methods to parameterize your Playwright scripts:

Using Array of Values

The first approach to parameterizing Playwright specs is straightforward:

  1. Declare an array of credentials,
  2. Loop over each login/password pair,
  3. Execute the test for each value.

Create a file named petstore-array.spec.ts with the following content:

import {test} from '@playwright/test';

const credentials = [
    {login: 'user1', password: 'pass'},
    {login: 'user2', password: 'pass'},
    {login: 'j2ee', password: 'j2ee'},
]

for (const loginPassword of credentials) {
    test(`test with ${loginPassword.login}`, async ({page}) => {
        await page.goto('https://petstore.octoperf.com/');
        await page.getByRole('link', {name: 'Enter the Store'}).click();
        await page.getByRole('link', {name: 'Sign In'}).click();
        await page.locator('input[name="username"]').click();
        await page.locator('input[name="username"]').fill(loginPassword.login);
        await page.locator('input[name="password"]').click();
        await page.locator('input[name="password"]').fill(loginPassword.password);
        await page.getByRole('button', {name: 'Login'}).click();
        await page.locator('#SidebarContent').getByRole('link').first().click();
        await page.getByRole('link', {name: 'FI-SW-01'}).click();
        await page.getByRole('link', {name: 'EST-1'}).click();
        await page.getByRole('link', {name: 'Add to Cart'}).click();
    });
}

Running Playwright for this script will show you that 3 tests are run: one for each value of the credentials array:

ubuntu@pop-os:~/Dev/workspaces/playwright-petstore$ npx playwright test tests/petstore-array.spec.ts

Running 3 tests using 3 workers
  3 passed (1.9s)

To open last HTML report run:

  npx playwright show-report

This is confirmed when opening the generated HTML report, you can see the 3 executions:

Playwright Report Array

While this solution allows to execute the same test with several values, these are internal and cannot be easily updated by an external tool like an automation framework. Using environment variables is the go-to solution for such use case.

Using Environment Variables

Let's use environment variables to parameterize your script. You can pass variables at runtime, allowing you to change configurations without modifying the script.

Create a new test file named petstore-env.spec.ts with the updated following content:

import {test} from '@playwright/test';

test(`test with ${process.env.LOGIN}`, async ({page}) => {
    console.log(process.env.LOGIN, process.env.PASSWORD);
    await page.goto('https://petstore.octoperf.com/');
    await page.getByRole('link', {name: 'Enter the Store'}).click();
    await page.getByRole('link', {name: 'Sign In'}).click();
    await page.locator('input[name="username"]').click();
    await page.locator('input[name="username"]').fill(process.env.LOGIN);
    await page.locator('input[name="password"]').click();
    await page.locator('input[name="password"]').fill(process.env.PASSWORD);
    await page.getByRole('button', {name: 'Login'}).click();
    await page.locator('#SidebarContent').getByRole('link').first().click();
    await page.getByRole('link', {name: 'FI-SW-01'}).click();
    await page.getByRole('link', {name: 'EST-1'}).click();
    await page.getByRole('link', {name: 'Add to Cart'}).click();
});

In NodeJs, environment variables are read using the syntax process.env.VARIABLE_NAME. Here we read the user login with process.env.LOGIN and the userr password with process.env.PASSWORD.

The easiest way to pass environment variables to Playwright is to set them directly in the command line: LOGIN=j2ee PASSWORD=j2ee npx playwright test tests/petstore-env.spec.ts

Another solution is to create a file name .env that declares the variable values:

LOGIN=j2ee
PASSWORD=j2ee

The dotenv library can then be used to read them before the Playwright execution. Simply uncomment the following line in the configuration file playwright.config.ts:

/**
 * Read environment variables from file.
 * https://github.com/motdotla/dotenv
 */
require('dotenv').config();

And install the dotenv package with the command npm install dotenv.

When running your Playwright script, the values are read from the file, no need to set them in the command line. The line console.log(process.env.LOGIN, process.env.PASSWORD); in the script displays the values in the console: j2ee j2ee :

ubuntu@pop-os:~/Dev/workspaces/playwright-petstore$ npx playwright test tests/petstore-env.spec.ts

Running 1 test using 1 worker
[chromium]  petstore-env.spec.ts:3:5  test with j2ee
j2ee j2ee
  1 passed (2.0s)

To open last HTML report run:

  npx playwright show-report

Using CSV Variables

To use a CSV variable in a Playwright script, you can follow these steps:

  1. Read the CSV File: You need to read the CSV file and parse its content in your Playwright script. You can use libraries like csv-parser for Node.js to read and parse CSV files. First, install the libraries:
npm install path fs csv-parse
  1. Load and Parse the CSV Data: In your Playwright script, use the csv-parser library to read and parse the data from the CSV file. Here's an example of how to do this:
import {test} from '@playwright/test';
import {parse} from 'csv-parse/sync';
import * as fs from "fs";
import * as path from "path";

const credentials = parse(fs.readFileSync(path.join(__dirname, 'credentials.csv')), {
    columns: true,
    skip_empty_lines: true
});
  1. Use CSV Data in Playwright Automation: You can now use the parsed CSV data in your Playwright automation scripts. For example, you can iterate through the credentials and use them in your Playwright test:
for (const loginPassword of credentials) {
    test(`test with ${loginPassword.login}`, async ({page}) => {
        [...]
        await page.locator('input[name="username"]').fill(loginPassword.login);
        await page.locator('input[name="password"]').fill(loginPassword.password);
        [...]
    });
}

In this example, we assume that your CSV file credentials.csv has a column named 'login', another column named ' password', and that each row contains a couple of credentials used to connect to the Petstore Web-application.

"login","password"
"user1","pass"
"user2","pass"
"j2ee","j2ee"

By following these steps, you can read and use data from a CSV file in your Playwright automation scripts. This approach is helpful when you need to automate repetitive tasks on a large number of web pages or when you want to parameterize your scripts with data from an external source.

ubuntu@pop-os:~/Dev/workspaces/playwright-petstore$ npx playwright test tests/petstore-csv.spec.ts

Running 3 tests using 3 workers
  3 passed (2.1s)

To open last HTML report run:

  npx playwright show-report

You can check the generated HTML report for executions: as defined in the CSV file, the test is run for users user1, user2 and j2ee.

Playwright Report CSV

Here is the complete sample file petstore-csv.spec.ts content:

import {test} from '@playwright/test';
import {parse} from 'csv-parse/sync';
import * as fs from "fs";
import * as path from "path";

const credentials = parse(fs.readFileSync(path.join(__dirname, 'credentials.csv')), {
    columns: true,
    skip_empty_lines: true
});

for (const loginPassword of credentials) {
    test(`test with ${loginPassword.login}`, async ({page}) => {
        await page.goto('https://petstore.octoperf.com/');
        await page.getByRole('link', {name: 'Enter the Store'}).click();
        await page.getByRole('link', {name: 'Sign In'}).click();
        await page.locator('input[name="username"]').click();
        await page.locator('input[name="username"]').fill(loginPassword.login);
        await page.locator('input[name="password"]').click();
        await page.locator('input[name="password"]').fill(loginPassword.password);
        await page.getByRole('button', {name: 'Login'}).click();
        await page.locator('#SidebarContent').getByRole('link').first().click();
        await page.getByRole('link', {name: 'FI-SW-01'}).click();
        await page.getByRole('link', {name: 'EST-1'}).click();
        await page.getByRole('link', {name: 'Add to Cart'}).click();
    });
}

Using OctoPerf Load Testing Platform

OctoPerf is a performance testing and load testing platform designed for evaluating the performance, scalability, and reliability of web applications and systems. It provides a comprehensive set of tools and features that enable developers, testers, and organizations to simulate real-world user activity, measure system performance, and identify performance bottlenecks.

The SaaS version is available at https://api.octoperf.com/ui/.

To use a CSV variable in a Playwright script executed in the OctoPerf load testing Cloud, you can follow these steps:

  1. Create a CSV Variable: Assuming you have created an account and a Project, you need to create a blank CSV Variable.

OctoPerf Create CSV Variable

  1. Upload a credentials CSV file: Download the sample credentials.csv file and upload it in your CSV Variable.

OctoPerf Upload CSV File

  1. Check the CSV Variable columns: The login and password columns should appear at the bottom of your CSV Variable editor.

OctoPerf CSV Columns

  1. Create a Playwright Virtual User using the following specs file.
import {test} from '@playwright/test';

test(`test with ${process.env.login}`, async ({page}) => {
    console.log(process.env.login, process.env.password);
    await page.goto('https://petstore.octoperf.com/');
    await page.getByRole('link', {name: 'Enter the Store'}).click();
    await page.getByRole('link', {name: 'Sign In'}).click();
    await page.locator('input[name="username"]').click();
    await page.locator('input[name="username"]').fill(process.env.login);
    await page.locator('input[name="password"]').click();
    await page.locator('input[name="password"]').fill(process.env.password);
    await page.getByRole('button', {name: 'Login'}).click();
    await page.locator('#SidebarContent').getByRole('link').first().click();
    await page.getByRole('link', {name: 'FI-SW-01'}).click();
    await page.getByRole('link', {name: 'EST-1'}).click();
    await page.getByRole('link', {name: 'Add to Cart'}).click();
});
  1. Validate you Virtual User: Running 3 iterations of the created virtual user in Debug Mode should result in 3 validation results, each one using a different line of the uploaded CSV file.

OctoPerf Debug stdout

One of the advantages of running your Playwright scripts through OctoPerf is that CSV files are automatically split across injectors when running your tests using different user profiles.

Parameterizing Playwright Projects

Another approach to test variabiliszation in Playwright to parameterize at the Project level.

Indeed, Playwright supports running multiple test projects at the same time. They are defined in the configuration playwright.config.ts file. Concretely we are going to define a type for the user credentials with default values, then override it in the project definition.

Let's start by creating a file petstore-test.ts in the tests folder of your Playwright Node.js project:

import {test as base} from '@playwright/test';

export type PetstoreTestOptions = {
    login: string;
    password: string;
};

export const petstoreTest = base.extend<PetstoreTestOptions>({
    // Define an option and provide a default value.
    // We can later override it in the config.
    login: ['user1', {option: true}],
    password: ['pass', {option: true}],
});

The type PetstoreTestOptions defines a credentials type with a login and a password. The constant petstoreTest extends the default Playwright test by adding options for the login and password.

Create the petstore.spec.ts spec file with the following content:

import {petstoreTest} from "./petstore-test";

petstoreTest('test', async ({page, login, password}) => {
    console.log(login, password);
    await page.goto('https://petstore.octoperf.com/');
    await page.getByRole('link', {name: 'Enter the Store'}).click();
    await page.getByRole('link', {name: 'Sign In'}).click();
    await page.locator('input[name="username"]').click();
    await page.locator('input[name="username"]').fill(login);
    await page.locator('input[name="password"]').click();
    await page.locator('input[name="password"]').fill(password);
    await page.getByRole('button', {name: 'Login'}).click();
    await page.locator('#SidebarContent').getByRole('link').first().click();
    await page.getByRole('link', {name: 'FI-SW-01'}).click();
    await page.getByRole('link', {name: 'EST-1'}).click();
    await page.getByRole('link', {name: 'Add to Cart'}).click();
});

The login and password current values are given to the script as test parameters async ({page, login, password}).

Running the test without specifying values in the project will use the defaults (specified in petstore-test.ts):

ubuntu@pop-os:~/Dev/workspaces/playwright-petstore$ npx playwright test tests/petstore.spec.ts

Running 1 test using 1 worker
[chromium]  petstore.spec.ts:3:13  test
user1 pass
  1 passed (2.0s)

To open last HTML report run:

  npx playwright show-report

Here the console logs user1 pass.

Let's update the Playwright configuration file playwright.config.ts to set a value for the login:

import {defineConfig, devices} from '@playwright/test';
import {PetstoreTestOptions} from "./tests/petstore-test";

export default defineConfig<PetstoreTestOptions>({
    /* Configure projects for major browsers */
    projects: [
        {
            name: 'chromium',
            use: {
                login: 'user2',
                ...devices['Desktop Chrome']
            },
        },
    ],
});

Three things must be updated here:

  • PetstoreTestOptions must be imported import {PetstoreTestOptions} from "./tests/petstore-test";.
  • It must be used as a type parameter for the configuration export default defineConfig<PetstoreTestOptions>.
  • The login must be defined in the project section projects: [{name: 'chromium', use: { login: 'user2' }}]

Running the test will now show user2 pass in the console logs:

ubuntu@pop-os:~/Dev/workspaces/playwright-petstore$ npx playwright test tests/petstore.spec.ts

Running 1 test using 1 worker
[chromium]  petstore.spec.ts:3:13  test
user2 pass
  1 passed (2.0s)

To open last HTML report run:

  npx playwright show-report

Using Loops and Random Selectors

Until now, we only parameterized the credentials used by our virtual user to connect to the PetStore website. Let's use loops and random values to make it add random items to the e-commerce cart.

The following file petstore-loop.spec.ts defines a method addRandomItemToCart that does exactly that. The main test 'test' goes to the PetStore home and enters it. A for loop then adds 3 random item to the cart.

import {test, expect, Page} from '@playwright/test';

test('test', async ({page}) => {
    await page.goto('https://petstore.octoperf.com/');
    await page.getByRole('link', {name: 'Enter the Store'}).click();
    for (let i = 0; i < 3; i++) {
        await addRandomItemToCart(page);
    }
});


const addRandomItemToCart = async (page: Page) => {
    await page.locator('#LogoContent').getByRole('link').click();
    const categories = page.locator('#SidebarContent').getByRole('link');
    const categoriesCount = await categories.count();
    expect(categoriesCount).toBe(5);
    await categories.nth(Math.floor(Math.random() * categoriesCount)).click();

    const products = page.getByRole('table').getByRole('link');
    const productsCount = await products.count();
    expect(productsCount).toBeGreaterThan(0);
    await products.nth(Math.floor(Math.random() * productsCount)).click();

    const items = page.getByRole('table').getByRole('link', {name: 'Add to Cart'});
    const itemsCount = await items.count();
    expect(itemsCount).toBeGreaterThan(0);
    await items.nth(Math.floor(Math.random() * itemsCount)).click();

    await expect(page.getByRole('heading', {name: 'Shopping Cart'})).toBeVisible();
}
  1. Links are extracted from each page using locators: const locator = page.locator('container').getByRole('link');.

  2. The number of link is then computed using the count() method: const count = await locator.count();.

  3. Finally, a random link is clicked: await locator.nth(Math.floor(Math.random() * count)).click();.

Let's run this script with traces ON:

ubuntu@pop-os:~/Dev/workspaces/playwright-petstore$ npx playwright test tests/petstore-loop.spec.ts --trace on

Running 1 test using 1 worker
  1 passed (6.1s)

To open last HTML report run:

  npx playwright show-report

And open the generated trace with the command line npx playwright show-trace test-results/petstore-loop-test-chromium/trace.zip:

Petstore Trace

We can see in the Actions on the left and in the timeline at the top that 3 items were added to the cart:

Petstore Cart

Conclusion

Parameterizing Playwright scripts is a fundamental practice that enhances the efficiency, coverage, and maintainability of your browser automation tests. By choosing the method that best suits your testing needs, you can ensure that your web applications are thoroughly tested across various scenarios, configurations, and inputs. Whether you opt for external data sources, environment variables, or command-line arguments, parameterization empowers you to create robust, scalable, and adaptable Playwright test scripts.

Want to become a super load tester?
Request a Demo