Playwright Test Automation: POM, BDD & DDT Architectures

5 min readMar 24, 2025

In this post, I will briefly discuss the most commonly used test automation architectures: Page Object Model (POM), Data-Driven Testing (DDT), and Behavior-Driven Development (BDD). I will also share examples showing how you can implement these architectures using Playwright. Along with the advantages of each architecture, I will explain how they are applied in real-world scenarios with sample code.

To help you better understand these implementations, I’ve created a GitHub repository with examples. You can clone the repository using the command below to view and run it:

git clone https://github.com/senakorkmaz/Test-automation-architecture.git

Repository structure:

Test-automation-architecture/

├── tests/ # Playwright test scenarios
│ ├── bddTests/ # BDD Testing (Cucumber) examples
│ │ ├── features/ # Test scenarios (written in Gherkin)
│ │ │ └── login.feature # Login scenario
│ │ ├── steps/ # Step definitions
│ │ │ └── loginSteps.js # Login test steps
│ │ ├── world.js # Cucumber World class
│ │ └── cucumber.js # Cucumber config file
│ ├── ddtTests/ # Data-Driven Testing (DDT) examples
│ │ └── loginDDT.spec.js # Login test
│ ├── pomTests/ # Page Object Model (POM) examples
│ │ └── loginPOMTest.spec.js # Login test

├── pages/ # Page Object Model (POM) classes
│ └── loginPage.js # Login page POM class

├── testData/ # Test data (for Data-Driven Testing)
│ └── users.json # User data (for login)

├── playwright.config.js # Playwright config file
├── package.json # Dependencies and config file
└── README.md # Project overview and documentation

Now, let’s examine how this structure works and how each architecture is implemented.

What is Data-Driven Testing (DDT)?

Data-Driven Testing (DDT) is an approach where the same test scenario is executed with different data sets. Instead of writing the test scenario manually each time, we can dynamically run tests using data from external sources (JSON, CSV, Excel, etc.).

For example, we can use a JSON file containing different usernames and passwords to repeat login tests. This approach speeds up the testing process, simplifies maintenance, and allows for more comprehensive tests.

→ loginDDT.spec.js

const { test, expect } = require('@playwright/test');
const LoginPage = require('../../pages/loginPage');
const users = require('../../testData/users.json').users;

test.describe('Data-Driven Login Tests', () => {
users.forEach(data => {
test(`Login test for ${data.username}`, async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.navigate();
await loginPage.login(data.username, data.password);

await expect(page).toHaveURL(/inventory/);
});
});
});

In the Playwright test code written for DDT:

  • User information is read from a JSON file.
  • The test is automatically executed for each user.
  • Page management is handled using a Page Object Model (POM).
  • The URL the page is redirected to is verified in case of a successful login.

What is Page Object Model (POM)?

The Page Object Model (POM) is a design pattern used to abstract and manage page interactions in test scenarios. In this approach, each web page is modeled as an object (class), and all actions related to that page (e.g., filling out forms, clicking buttons) are defined within that object.

For example, instead of writing login actions in every test file, we can define them in a LoginPage class.

→ loginPage.js

class LoginPage {
constructor(page) {
this.page = page;
this.usernameField = '#user-name';
this.passwordField = '#password';
this.loginButton = '#login-button';
}

async navigate() {
await this.page.goto('https://www.saucedemo.com/');
}

async login(username, password) {
await this.page.fill(this.usernameField, username);
await this.page.fill(this.passwordField, password);
await this.page.click(this.loginButton);
}
}
module.exports = LoginPage;
  • Constructor:
    The constructor of the class takes the page object and defines the CSS selectors for the elements on the login page (username, password, login button).
  • navigate():
    This function redirects the user to the login page using the goto method.
  • login(username, password):
    This function performs the login operation with the provided username and password. It fills in the fields in the login form and clicks the login button.

This class manages page interactions centrally according to the Page Object Model (POM) architecture, making it reusable in tests.

→ loginPOMTest.spec.js

const { test, expect } = require('@playwright/test');
const LoginPage = require('../../pages/loginPage');

test('User should be able to login', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.navigate();
await loginPage.login('standard_user', 'secret_sauce');

await expect(page).toHaveURL(/inventory/);
});

In the Playwright test code written for POM:

  • An object is created from the LoginPage class. This class contains all interactions related to the login page.
  • The navigate function is used to go to the page. It opens the login page..
  • The login function is called to perform login with the username and password. The user information is defined in the test code.
  • A successful login check is performed. When the page is redirected to the correct URL, it confirms that the login operation was successful.

What is Behavior-Driven Development (BDD)?

It is an approach used to write behavior-driven tests in the software development process. BDD facilitates communication between business units (Business Analyst, Product Manager, Product Owner, etc.) and developers, as test scenarios directly express business requirements. Scenarios are typically written in the “Given-When-Then” format and defined using the Gherkin language. Now, let’s take a look at our example code together.

→ loginSteps.js

const { Given, When, Then } = require('@cucumber/cucumber');
const { expect } = require('@playwright/test');
const LoginPage = require('../../../pages/loginPage');

let loginPage;

Given('User is on the login page', async function () {
loginPage = new LoginPage(this.page);
await loginPage.navigate();
});

When('User enters {string} and {string}', async function (username, password) {
await loginPage.login(username, password);
});

Then('User should see the product inventory page', async function () {
await expect(this.page).toHaveURL(/inventory/);
});

It is used to implement the steps in the feature file written in Gherkin syntax. The Given, When, and Then steps correspond to the scenario statements in Gherkin. These steps are implemented using Page Object Model (POM) functions. For example, the navigate() function is used in the Given step to go to the page, the login() function is used in the When step to perform the login, and the expect() function is used in the Then step to validate the URL. This way, we establish a consistent link between the Gherkin scenarios and the tests.

→ login.feature

Feature: User Login

Scenario: Successful Login
Given User is on the login page
When User enters "standard_user" and "secret_sauce"
Then User should see the product inventory page

The Given, When, and Then steps correspond to the relevant functions in the loginSteps.js file. This approach ensures that tests align with Gherkin scenarios, making them understandable even for non-technical individuals.

You can add new feature files to create new scenarios and test different cases. For example, you can easily test different scenarios with files like login-invalid-credentials.feature or login-locked-out.feature.

When to Use Which Architecture?

  • POM (Page Object Model): Used for medium-to-large scale UI tests and increases readability by preventing code duplication.
  • BDD (Behavior-Driven Development): Preferred in collaboration-driven projects, allowing even non-technical people to write tests.
  • DDT (Data-Driven Testing): Used when testing with different data sets. It’s easy to add new data.

To discover which architecture is best for you, try different methods. You can enhance your applications by exploring the repository on GitHub. If you have any questions, I’m always happy to help! 💞

--

--

Senanur KORKMAZ
Senanur KORKMAZ

Written by Senanur KORKMAZ

Software Quality Assurance Engineer at Insider

Responses (1)