---
title: 'Maximizing Testing Efficiency: Parameterizing Playwright Scripts'
url: https://blog.octoperf.com/maximizing-testing-efficiency-parameterizing-playwright-scripts/
date: '2023-10-13'
authors:
- gpereira
categories:
- Real-Browser
tags:
- Playwright
- Variable
- CSV
description: Create robust, scalable, and adaptable Playwright test scripts via parameterization. Explore several approaches to parameterizing Playwright scripts such as external data sources, environment variables, command-line arguments and more.
---

# 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.


## 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](https://blog.octoperf.com/getting-started-with-playwright-a-comprehensive-guide/#prerequisites) to install the
   Playwright testing environment.
2. **A Playwright Automation Script:** You'll need
   a [Playwright script](https://blog.octoperf.com/playwright-test-generator/#fix-your-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.

```typescript
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](https://petstore.octoperf.com/).

We will start by [parameterizing login and password](#methods-to-parameterize-playwright-scripts) used to connect to the
website.
Then we will se [how to loop over random items](#using-loops-and-random-selectors) and add them to the buyer's cart.

## Methods to Parameterize Playwright Scripts

[Parameterizing Playwright tests](https://playwright.dev/docs/test-parameterize) 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 and array of values](#using-array-of-values)** to execute a test with varying inputs.
* **[Using environment variables](#using-environment-variables)** to execute a test using external inputs (set in a
  _.env_ file).
* **[Using a CSV file](#using-csv-variables)** to read variable inputs from
  a [Comma-separated values](https://en.wikipedia.org/wiki/Comma-separated_values) file.
* **[Using OctoPerf load-testing platform](#using-octoperf-load-testing-platform)** to run Playwright scripts in the
  Cloud with sharded inputs.

### Using Array of Values

The first approach to parameterizing Playwright specs is straightforward:

1. Declare an array of credentials,
2. [Loop over each](https://www.typescriptlang.org/docs/handbook/iterators-and-generators.html#forof-statements)
   login/password pair,
3. Execute the test for each value.

Create a file named _petstore-array.spec.ts_ with the following content:

```typescript
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:

```bash
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](https://blog.octoperf.com/img/blog/playwright/playwright-parameterized-test/petstore-array-report.png "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](#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:

```typescript
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](https://nodejs.org/learn/command-line/how-to-read-environment-variables-from-nodejs)
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:

```text
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_:

```typescript
/**
 * 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` :

```bash
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](https://playwright.dev/docs/test-parameterize#create-tests-via-a-csv-file),
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:

```bash
npm install path fs csv-parse
```

2. **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:

```typescript
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
});
```

3. **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:

```typescript
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_](https://blog.octoperf.com/img/blog/playwright/playwright-parameterized-test/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.

```text
"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**.

```bash
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](https://blog.octoperf.com/img/blog/playwright/playwright-parameterized-test/petstore-csv-report.png "Playwright Report CSV")

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

```typescript
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/](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](https://www.youtube.com/watch?v=aQyfa4hzLok), you need to
   create a blank CSV Variable.

![OctoPerf Create CSV Variable](https://blog.octoperf.com/img/blog/playwright/playwright-parameterized-test/octoperf-create-csv-variable.png "OctoPerf Create CSV Variable")

2. **Upload a credentials CSV file**: Download the sample [_credentials.csv_](https://blog.octoperf.com/img/blog/playwright/playwright-parameterized-test/credentials.csv) file and upload it in your CSV Variable.

![OctoPerf Upload CSV File](https://blog.octoperf.com/img/blog/playwright/playwright-parameterized-test/octoperf-upload-csv-file.png "OctoPerf Upload CSV File")

3. **Check the CSV Variable columns**: The _login_ and _password_ columns should appear at the bottom of your CSV
   Variable editor.

![OctoPerf CSV Columns](https://blog.octoperf.com/img/blog/playwright/playwright-parameterized-test/octoperf-csv-columns.png "OctoPerf CSV Columns")

4. [Create a Playwright Virtual User](https://api.octoperf.com/doc/design/create-virtual-user/import-playwright-node-project/)
   using the following specs file.

```typescript
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();
});
```

5. **Validate you Virtual User**: Running 3 iterations of the
   created [virtual user in Debug Mode](https://api.octoperf.com/doc/design/edit-virtual-user/validation/#configuration)
   should result in 3 validation results, each one using a different line of the uploaded CSV file.

![OctoPerf Debug stdout](https://blog.octoperf.com/img/blog/playwright/playwright-parameterized-test/octoperf-debug-stdout.png "OctoPerf Debug stdout")

One of the advantages of **running your Playwright scripts through OctoPerf** is
that [CSV files are automatically split across injectors](https://api.octoperf.com/doc/design/edit-virtual-user/configuration/variables/csv/#csv-file-splitting)
when running your tests using different user profiles.

## Parameterizing Playwright Projects

Another approach to test variabiliszation in Playwright
to [parameterize at the Project level](https://playwright.dev/docs/test-parameterize#parameterized-projects).

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:

```typescript
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:

```typescript
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_):

```bash
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:

```typescript
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:

```bash
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.

```typescript
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](https://blog.octoperf.com/browser-automation-debug-with-playwright-trace-viewer/):

```bash
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](https://blog.octoperf.com/img/blog/playwright/playwright-parameterized-test/petstore-loop-trace.png "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](https://blog.octoperf.com/img/blog/playwright/playwright-parameterized-test/petstore-loop-cart.png "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.
