Query parameters are a way to pass information to an API flexibly and simply. They are added to the end of the API endpoint URL as a series of key-value pairs. To append query params to the end of a URL, a ‘?’ Is added followed immediately by a query parameter.
Handling HTTP query parameters in Playwright typically involves setting up your request with the desired parameters before navigating to a URL. Playwright provides methods to easily construct and use URLs with query parameters.
The following prerequisites are required to be installed on the machine to begin with a smooth setup and installation.
Java 11 or higher
IntelliJ IDE or any other IDE to create a project
Maven
Dependency List
Playwright – 1.57.0
Java 17
Maven – 3.9.6
TestNG – 7.11.0
Maven Compiler Plugin – 3.15.0
Maven Surefire Plugin – 3.5.4
Implementation Steps
1. Create a new Maven Project
The first step in setup is to create a new Maven project. I will be using IntelliJ in this tutorial. The following steps need to be followed to create a new Maven project :
Open IntelliJ, Navigate to File >> New >> Project
2. In the New Project window, enter the following details:
Name of the Project – Playwright_API_Demo
Location/path where the project needs to be saved – Documents/Playwright (my location)
Select JDK version — I am using JDK 17
Archetype — Search for quickstart and select maven-archetype-quickstartfrom the results
Click on the Create button to create the project.
This will create a project as shown below in the IntelliJ.
2. Setup Playwright with Java and TestNG
Add the Playwright and TestNG dependencies to the pom.xml. The latest dependency can be found from here.
After adding the dependency, refresh the project. We can see that the Playwright jar files are downloaded in External Libraries.
3. Create API tests
Create Setup Methods
@BeforeMethod: This annotation indicates that `setUp()` will run before each test method, ensuring that the Playwright environment and API request context are properly initialized.
createAPIRequestContext(): Sets up the API request context with a base URL (`https://jsonplaceholder.typicode.com`) and common headers (like “Accept: application/json”) that will be used for all requests.
`@AfterMethod`: Ensures that resources are properly disposed of after every test method execution by calling `request.dispose()` and `playwright.close()`. This avoids resource leaks and ensures clean execution for subsequent tests.
@AfterMethod
void tearDown() {
if (request != null) {
request.dispose();
}
if (playwright != null) {
playwright.close();
}
}
Query Parameter
The corresponding API Test in Playwright Framework.
@Test
public void getCommentsByPostId() {
// GET /comments?postId=1
APIResponse response = request.get("/comments",
RequestOptions.create().setQueryParam("postId", "1"));
Assert.assertEquals(response.status(), 200, "Expected 200 for GET /comments?postId=1");
String body = response.text();
System.out.println("GET Response: " + body);
Gson gson = new Gson();
JsonObject[] posts = gson.fromJson(body, JsonObject[].class);
Assert.assertTrue(posts.length > 0, "Expected at least one post for postId=1");
Assert.assertEquals(posts[0].get("postId").getAsInt(), 1, "Expected postId=1 in first result");
Assert.assertEquals(posts[0].get("id").getAsInt(), 1, "Expected id=1 in first result");
Assert.assertEquals(posts[0].get("email").getAsString(), "Eliseo@gardner.biz", "Expected email is incorrect for first result" + posts[0].get("email").getAsString());
}
Explanation
1.Sending an API GET Request
Sends an HTTP GET request to the `/comments` endpoint with a query parameter `postId` set to `“1”`.
Verifies that the response status code is 200 (OK).
Assert.assertEquals(response.status(), 200, "Expected 200 for GET /comments?postId=1");
3. Read the response body
Prints the entire response content to the console for debugging or logging.
String body = response.text();
System.out.println("GET Response: " + body);
4. Parsing the JSON Response
Parses the response body into an array of `JsonObject`. This assumes the API returns a JSON array of objects, each representing a comment.
Gson gson = new Gson();
JsonObject[] posts = gson.fromJson(body, JsonObject[].class);
5. Validate the Response data
Checks that there is at least one comment in the response, indicating the API return is not empty.
Assert.assertTrue(posts.length > 0, "Expected at least one post for postId=1");
Verifies that the `postId` in the first comment object is `1`.
Verifies that the `id` in the first comment object is `1`.
Verifies that the `email` in the first comment object is `Eliseo@gardner.biz`.
Assert.assertEquals(posts[0].get("postId").getAsInt(), 1, "Expected postId=1 in first result");
4. Test Execution through IntelliJ
Go to the Test class and right-click and select Run ‘API_Tests’. The tests will run as IntelliJ tests.
Below is the test execution result.
5. Run the tests using the command line
Use the below command to run the tests using the command line.
mvn clean test
The output of the above program is
6. TestNG Report Generation
TestNG creates a target directory that contains index.html report.
Right-click on the index.html and open it in the browser.
Summary
Playwright: Utilized for its ability to handle HTTP requests and execute browser interactions. This code leverages Playwright for making API calls rather than web UI interactions.
Gson: Simplifies the parsing of JSON responses into Java objects, allowing easy assertion of specific fields.
TestNG: Provides the testing framework features such as test annotations and assertions used to validate the API responses.
An HTTP request is a message sent by a client (usually a web browser or a tool like cURL or Postman) to a server, requesting a resource or action to be performed. This is a fundamental part of the Web’s client-server communication model. The HTTP protocol defines how requests and responses are formatted and transmitted over the Internet.
System requirements
The following prerequisites are required to be installed on the machine to begin with a smooth setup and installation.
Java 11 or higher
IntelliJ IDE or any other IDE to create a project
Maven
Dependency List
Playwright – 1.57.0
Java 17
Maven – 3.9.6
TestNG – 7.11.0
Maven Compiler Plugin – 3.15.0
Maven Surefire Plugin – 3.5.4
Implementation Steps
1. Create a new Maven Project
The first step in setup is to create a new Maven project. I will be using IntelliJ in this tutorial. The following steps need to be followed to create a new Maven project :
Open IntelliJ, Navigate to File >> New >> Project
2. In the New Project window, enter the following details:
Name of the Project – Playwright_API_Demo
Location/path where the project needs to be saved – Documents/Playwright (my location)
Select JDK version — I am using JDK 17
Archetype — Search for quickstart and select maven-archetype-quickstartfrom the results
Click on the Create button to create the project.
This will create a project as shown below in the IntelliJ.
2. Setup Playwright with Java and TestNG
Add the Playwright and TestNG dependencies to the pom.xml. The latest dependency can be found from here.
After adding the dependency, refresh the project. We can see that the Playwright jar files are downloaded in External Libraries.
3. Create API tests in
Create Setup Methods
@BeforeMethod: This annotation indicates that setUp() will run before each test method, ensuring that the Playwright environment and API request context are properly initialized.
createAPIRequestContext(): Sets up the API request context with a base URL (https://jsonplaceholder.typicode.com) and common headers (like “Accept: application/json”) that will be used for all requests.
@AfterMethod: Ensures that resources are properly disposed of after every test method execution by calling request.dispose()and playwright.close(). This avoids resource leaks and ensures clean execution for subsequent tests.
@AfterMethod
void tearDown() {
if (request != null) {
request.dispose();
}
if (playwright != null) {
playwright.close();
}
}
GET Request
What is GET Request?
The GET HTTP method requests a representation of the specified resource. Requests using GET should only be used to request data and shouldn’t contain a body.
The corresponding API Test in Playwright Framework.
@Test
public void getUserById() {
APIResponse response = request.get("/posts/1");
System.out.println("GET Response :" + response.text());
Assert.assertEquals(response.status(), 200, "Expected 200 for GET /posts/1");
Gson gson = new Gson();
JsonObject jsonResponse = gson.fromJson(response.text(), JsonObject.class);
Assert.assertEquals(jsonResponse.get("userId").getAsInt(), 1, "Expected userId=1 in response body");
Assert.assertEquals(jsonResponse.get("id").getAsInt(), 1, "Expected id=1 in response body");
}
Explanation
We sends a GET request to retrieve a specific post.
Verifies that the response status code is 200 (OK).
Parses the response JSON to check for expected values (`userId` and `id` both equal to 1).
POST Request
What is POST Request?
The POST HTTP method sends data to the server. The type of the body of the request is indicated by the Content-Type header
@Test
public void createUser() {
Map<String, Object> payload = new HashMap<>();
payload.put("title", "architect");
payload.put("body", "post test");
payload.put("userId", 100);
APIResponse response = request.post("/posts", RequestOptions.create().setData(payload));
System.out.println("POST Response :" + response.text());
Assert.assertEquals(response.status(), 201, "Expected 201 for POST /users");
Gson gson = new Gson();
JsonObject body = gson.fromJson(response.text(), JsonObject.class);
Assert.assertEquals(body.get("title").getAsString(), "architect");
Assert.assertEquals(body.get("body").getAsString(), "post test");
Assert.assertEquals(body.get("userId").getAsInt(), 100);
Assert.assertNotNull(body.get("id"), "Expected id in create response");
}
Explanation
Constructs a payload for creating a new post and sends a POST request.
Verifies that the response status code for successful creation (201).
Parses the response and asserts that the payload values match the expected results, ensuring the ID field is not null, indicating successful resource creation.
PUT Request
What is PUT Request?
The PUT HTTP method creates a new resource or replaces a representation of the target resource with the request content.
@Test
public void updateUser() {
APIResponse response = request.get("/posts/1");
System.out.println("GET Response before PUT Request:" + response.text());
Assert.assertEquals(response.status(), 200, "Expected 200 for GET /posts/1");
Map<String, Object> payload = new HashMap<>();
payload.put("id", 1);
payload.put("title", "business manager");
payload.put("body", "Implementing DataWareHouse Migration Project");
payload.put("userId", 50);
APIResponse response1 = request.put("/posts/1", RequestOptions.create().setData(payload));
System.out.println("PUT Response :" + response1.text());
Assert.assertEquals(response1.status(), 200, "Expected 200 for PUT /posts/1");
Gson gson = new Gson();
JsonObject body = gson.fromJson(response1.text(), JsonObject.class);
Assert.assertEquals(body.get("title").getAsString(), "business manager");
Assert.assertEquals(body.get("body").getAsString(), "Implementing DataWareHouse Migration Project");
Assert.assertEquals(body.get("userId").getAsInt(), 50);
Assert.assertNotNull(body.get("id"), "Expected id in create response");
}
Explanation
Retrieves a post using a GET request to ensure it exists.
Constructs a payload with updated information and sends a PUT request to modify the post.
Verifies that the response status code is 200 to verify successful update.
Verifies that the response JSON contains updated values as specified in the payload.
DELETE Request
What is Delete Request?
The DELETE HTTP method asks the server to delete a specified resource. Requests using DELETE should only be used to delete data and shouldn’t contain a body.
Confirms the successful deletion by checking for a 200 response status code.
7. Test Execution through IntelliJ
Go to the Test class and right-click and select Run ‘API_Tests’. The tests will run as IntelliJ tests.
Below is the test execution result.
8. Run the tests using the command line
Use the below command to run the tests using the command line.
mvn clean test
The output of the above program is
9. TestNG Report Generation
TestNG creates a target directory that contains index.html report.
Right-click on the index.html and open it in the browser.
Summary:
Playwright: Utilized for its ability to handle HTTP requests and execute browser interactions. This code leverages Playwright for making API calls rather than web UI interactions.
Gson: Simplifies the parsing of JSON responses into Java objects, allowing easy assertion of specific fields.
TestNG: Provides the testing framework features such as test annotations and assertions used to validate the API responses.
Running only the last failed tests in Playwright can be a useful strategy. It helps in quickly debugging failures. This approach ensures that intermittent issues are resolved.
Playwright provides a way to run tests from the last failed test file using the `–rerun` command-line option. This option is particularly useful in large test suites to focus specifically on the problematic tests.
First, execute your regular test suite. For example:
npx playwright test --project webkit
Below are the tests used in this example.
import { test, expect } from '@playwright/test';
test('has title', async ({ page, browserName }) => {
await page.goto('https://opensource-demo.orangehrmlive.com/');
console.log(`Running test on browser: ${browserName}`); // Print the browser name
await page.waitForTimeout(3000); // Wait for 3 seconds
// Expect a title "to contain" a substring.
await expect(page).toHaveTitle(/OrangeHRM/);
});
test('login', async ({ page, browserName }) => {
const username = 'Admin';
const password = 'admin123'
await page.goto('https://opensource-demo.orangehrmlive.com/');
console.log(`Running test on browser: ${browserName}`); // Print the browser name
// Fill in the username
await page.fill('input[name="username"]', username);
// Print the username
console.log(`Logging in with username: ${username}`);
// Fill in the password
await page.fill('input[name="password"]', password);
// Print the password
console.log(`Logging in with password: ${password}`);
// Click the login button - Use XPath to locate and click the login button
const loginButton = page.locator('//button[@type="submit" and contains(@class, "orangehrm-login-button")]');
await loginButton.click();
// Check if the page contains text Dashboard - Locate the element using XPath
const dashboardElement = await page.locator('//h6[contains(@class, "oxd-topbar-header-breadcrumb-module")]');
// Get the text content from the element
const dashboardText = await dashboardElement.textContent();
// Print the text
console.log(`Dashboard text: ${dashboardText}`);
expect(dashboardText).toContain('Dashboard1');
});
Go to the funnel shape icon called “Testing” or “Test Explorer View”. The failed test detail is mentioned in error-context.md file.
The output of the above program is
When the tests complete, the failed tests are noted in a file. You can run the last failed tests using the command:
npx playwright test --last-failed
It specifically targets the tests that did not pass in the last test run and executes them again. This is useful for quickly rechecking and fixing issues in tests without having to run the entire test suite again.
The output of the above program is
Below is the report generated and it shows that only 1 test is executed.
In this blog we will be utilizing Playwright Java for the execution of Parameterized Tests. We can parameterize tests using JUnit5 or TestNG. Here, we are using JUnit5 to parameterize the tests.
Parameterized testing is a testing technique. In this technique, the same test method is executed multiple times with different input values. It also has varying expected results.
Instead of writing many separate tests, you write one test and pass different data to it.
Why Parameterized testing is important?
1. Increased Test Coverage: Parameterized testing allows a single test method to run multiple times with different datasets. This ensures that various scenarios, inputs, and edge cases are covered, leading to thorough testing of the application’s functionality.
2. Maintainability: A parameterized test reduces code duplication since the same test logic is applied across different input data. This means fewer test scripts need maintenance. Any changes to the test logic affect all test cases using that method at the same time.
3. Scalability: Tests can be easily scaled by augmenting the set of input data. This helps account for new test scenarios. There is no need for rewriting test logic.
4. Improving Design Quality: Using parameterized tests encourages designing tests that focus on core logic. They foster better test architecture and design practices. These practices can handle diverse scenarios effectively.
Parameterized Tests using JUnit5
JUnit 5 offers robust support for parameterized testing through the @ParameterizedTest annotation. This feature allows us to run a single test method multiple times, each time with a different set of parameters. One of the ways to specify these parameters is by using the @CsvSource annotation.
The @CsvSource annotation allows us to specify the parameter values directly as a list of comma-separated values (CSV) within parentheses. Each line of CSV represents one set of input parameters for the test method.
The following prerequisites are required to be installed on the machine to begin with a smooth setup and installation.
Java 11 or higher
IntelliJ IDE or any other IDE to create a project
Maven
Browsers on which tests need to be run, like Chrome, Firefox, etc.
Implementation Steps
1. Create a new Maven Project
The first step in setup is to create a new Maven project. I will be using IntelliJ in this tutorial. The following steps need to be followed to create a new Maven project :
Open IntelliJ, Navigate to File >> New >> Project
2. In the New Project window, enter the following details:
Name of the Project – PageObjectModel_Playwright_JUnit5
Location/path where the project needs to be saved – Documents/Playwright (my location)
Select JDK version — I am using JDK 17
Archetype — Search for “quickstart” and select maven-archetype-quickstart from the results
Click on the Create button to create the project.
This will create a project as shown below in the IntelliJ.
2. Setup Playwright with Java and JUnit5
Add the Playwright and JUnit5 dependencies to the pom.xml. The latest dependency can be found from here.
After adding the dependency, refresh the project. We will see that the Playwright jar files are downloaded in External Libraries.
3. Project Structure for Maintainability
Creating a well-organized project structure is crucial for maintaining a scalable and efficient automation framework.
Page Classes (src/main/java/.../pages/): Each web page in your application should have a corresponding page class. This class encapsulates all the functionalities of the web page, following the Page Object Model.
Utility Classes (src/main/java/.../utils/): These classes can include various utilities like configuration readers, helper methods for common tasks, etc.
Test Classes (src/test/java/.../tests/): Here, you write your actual test cases, utilizing the page classes.
Maven Configuration (pom.xml): This file manages project dependencies, plugins, and other configurations.
4. Creating Page Object Classes
Page classes serve as an interface to a web page’s UI elements. Each page class corresponds to a page in your application, encapsulating the actions that can be performed on that page.
Identify the UI Elements: Determine all the elements on the web page that your test will interact with, such as text boxes, buttons, and links.
Define Selectors:Store the selectors for these elements in your page class. It’s a best practice to externalize these selectors, but for simplicity, we’ll define them directly in the class.
Implement Actions:Create methods for each action that can be performed on the page, like entering text, clicking buttons, etc.
Creating LoginPage Class
import com.microsoft.playwright.Page;
public class LoginPage {
private Page page;
//Locators
private final String usernameLocator = "input[name='username']";
private final String passwordLocator = "input[name='password']";
private final String submitButton = "button[type='submit']";
private final String errorMessage = "//p[contains(@class, 'oxd-text oxd-text--p oxd-alert-content-text')]";
//Constructor
public LoginPage(Page page){
this.page = page;
}
public void login(String username, String password) {
page.fill(usernameLocator,username);
page.fill(passwordLocator,password);
page.click(submitButton);
}
public String getErrorMessage(){
return page.textContent(errorMessage);
}
}
Creating DashboardPage Class
package org.example.pages;
import com.microsoft.playwright.Page;
public class DashboardPage {
private Page page;
//Locators
private final String dashboardHeading = "//h6[contains(@class, 'oxd-topbar-header-breadcrumb-module')]";
//Constructor
public DashboardPage(Page page) {
this.page = page;
}
// Methods
public String getHeading() {
return page.textContent(dashboardHeading);
}
}
5. Write the Test Scripts
Create a Test files under src/test/java. Use these page classes in your test scripts to perform end-to-end scenarios. This will keep your tests clean and focused on logic rather than details about the UI elements.
A typical test class includes:
Setup Method:Initializes the Playwright browser and other prerequisites before each test.
Test Methods: Individual test cases, each representing a different scenario.
Teardown Method:Closes the browser and performs any cleanup after each test.
Creating BaseTests
This class contains the common variables and methods used throughout the project, like setup and teardown methods.
package org.example.utils;
package com.example.utils;
import com.microsoft.playwright.*;
import org.junit.jupiter.api.*;
// Subclasses will inherit PER_CLASS behavior.
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class BaseTests {
// Shared between all tests in the class.
static Playwright playwright;
static Browser browser;
// New instance for each test method.
BrowserContext context;
protected Page page;
@BeforeAll
public static void launchBrowser() {
playwright = Playwright.create();
browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setHeadless(false));
}
@AfterAll
public static void closeBrowser() {
playwright.close();
}
@BeforeEach
public void createContextAndPage() {
context = browser.newContext();
page = context.newPage();
page.navigate("https://opensource-demo.orangehrmlive.com/web/index.php/auth/login");
}
@AfterEach
public void closeContext() {
context.close();
}
}
Explanation
In JUnit you can initialize Playwright and Browser in @BeforeAll method and destroy them in @AfterAll. Use @TestInstance(TestInstance.Lifecycle.PER_CLASS) annotation to make JUnit create one instance of a class for all test methods within that class (by default each JUnit will create a new instance of the class for each test method). Store Playwright and Browser objects in instance fields. They will be shared between tests. Each instance of the class will use its own copy of Playwright.
1. Playwright playwright:
Initialized to manage the browser automation session, allowing interactions with different browsers.
2. Browser browser:
Declared to store the browser instance that will be shared across test methods within the class.
Playwright playwright;
Browser browser = null;
3. BrowserContext context:
Created anew for each test method to simulate independent browser sessions. This ensures that the browser state doesn’t carry over between tests.
4. Page page:
Represents a single tab or window in a browser used for performing actions and assertions.
BrowserContext context;
Page page;
5. @BeforeAll launchBrowser():
This method is annotated with @BeforeAll, indicating it runs once before any test methods in the current class. The browser is launched in non-headless mode (setHeadless(false)), meaning an actual browser window is opened.
This method runs before each test method in the class, as indicated by the @BeforeEach annotation. It creates a new BrowserContext and a Page, ensuring each test runs in a clean, isolated state. The method also navigates to a login page (“https://opensource-demo.orangehrmlive.com/web/index.php/auth/login”), setting up the initial state for each test.
Runs after each test method, annotated with @AfterMethod. Closes the BrowserContext, effectively closing the browser tab and cleaning up resources.
@AfterAll
public static void closeBrowser() {
playwright.close();
}
8. @AfterEach closeContext()
Runs once after all test methods in the current class have executed, as indicated by the @AfterEach annotation. Closes the Playwright instance, freeing up all resources used for automation.
@AfterEach
public void closeContext() {
context.close();
}
Creating a Login Page Test Class:
We will be automating the following test scenario using Playwright Java and run them in Chromium
Verify that the user is able to login to the application successfully.
Verify the invalid username generates error message “Invalid credentials” for multiple set of incorrect credentials.
package com.example.tests;
import com.example.pages.DashboardPage;
import com.example.pages.LoginPage;
import com.example.utils.BaseTests;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class LoginTests extends BaseTests {
@ParameterizedTest
@CsvSource({
"admin123,admin123,Invalid credentials",
"admin,admin12,Invalid credentials",
"Admin,1234,Invalid credentials",
"12345,%^$£56,Invalid credentials"
})
public void unsuccessfulLogin(String username, String password, String expectedErrorMessage) {
LoginPage loginPage = new LoginPage(page);
loginPage.login(username,password);
String actualErrorMessage = loginPage.getErrorMessage();
Assertions.assertEquals(expectedErrorMessage, actualErrorMessage, "");
}
@Test
public void successfulLogin() {
LoginPage loginPage = new LoginPage(page);
loginPage.login("Admin","admin123");
DashboardPage dashboardPage = new DashboardPage(page);
String actualHeading = dashboardPage.getDashboardPageHeading();
Assertions.assertEquals("Dashboard",actualHeading, "Unable to login to the application");
}
}
Explanation
1. successfulLogin
This method enters correct credentials and login to the application.
2. unsuccessfulLogin
This method enter the invalid username and password and click on the Submit button. Then, it assert that the error message generated by the application is equal to “Invalid credentials”. Below code is used to parameterize the tests.
@CsvSource – This annotation specifies the input data for the parameterized test. Each line within the annotation is a pair of input strings followed by an expected result string, separated by commas.
Go to the Test class and right-click and select Run ‘LoginTests’. The tests will run as JUnit5 tests (in IntelliJ).
Below is the test execution result.
8. Run the tests using the command line
Use the below command to run the tests using the command line.
mvn clean test site
The output of the above program is
9. JUnit5 Report Generation
Maven Site Plugin creates a folder – site under the target directory.
Right-click on the Junit5 Report.html and open it in the browser.
Summary:
1. Utilize the `@ParameterizedTest` annotation to indicate that a method is a parameterized test.
2. Use the `@ValueSource`, `@CsvSource`, `@CsvFileSource`, or custom `@MethodSource` annotations to provide different sets of input data for each test run.
3. @CsvSource: Allows us to provide comma-separated values directly in the annotation for more complex parameter sets.
Playwright is a modern and powerful end-to-end testing framework developed by Microsoft, specifically designed for the automation of web applications. It supports programming languages like Java, Python, C#, and NodeJS. Playwright comes with Apache 2.0 License and is most popular with NodeJS with Javascript/Typescript.
Playwright is integrated with Java, Cucumber, and Junit5. This integration creates a robust and scalable test automation framework for modern web applications. It is a BDD-driven framework.
Page Object Model (POM) is a design pattern used in test automation. It enhances test maintenance and reduces code duplication. In this pattern, web pages are represented as classes. The various elements on the page are defined as variables in the class. All possible user interactions can then be implemented as methods in the class.
What is Cucumber?
Cucumber is Behavior Driven Development (BDD). It is a methodology that integrates non-technical stakeholders, testers, and developers into the same conversation. This is achieved by using clearly defined specifications.
In behavior-driven development with Cucumber, test cases are written in Gherkin syntax. They use a Given-When-Then structure. This structure closely mirrors user stories and business rules.
Benefits of Using Cucumber with Playwright
1. Readable, business-friendly tests: Cucumber’s Gherkin syntax (Given–When–Then) makes test scenarios easy to understand for non-technical stakeholders, bridging the gap between QA, developers, and business teams.
2. Better Test Maintainability: With standardized step definitions and modularized test architecture, scaling up your test suite is effortless. Altering the suite won’t harm the entire framework.
3. Clear reporting and traceability: Cucumber’s reports map test results directly to feature files. They map directly to scenarios. This makes it easier to track coverage and failures against requirements.
4. Cross-browser and cross-platform coverage: When combined with Playwright, Cucumber scenarios can be executed across Chromium, Firefox, and WebKit without changing feature files
5. Scales well in CI/CD pipelines: The combination supports parallel execution. It offers tagging and selective test runs. This makes it suitable for continuous testing in CI environments.
What is JUnit5?
JUnit5 is a test execution engine. It discovers and runs Cucumber tests. It enhances integration testing with advanced annotations, nested tests, and parameterized testing. It simplifies test execution in Maven, Gradle, and Spring Boot with better assertions, Java dependency injection, and parallel execution.
System requirements
The following prerequisites are required to be installed on the machine to begin with a smooth setup and installation.
Java 11 or higher
IntelliJ IDE or any other IDE to create a project
Maven
Browsers on which tests need to be run, like Chrome, Firefox, etc.
Cucumber is installed
High-Level Architecture
Setting Up Cucumber BDD Framework with Playwright and JUnit5
1. Create a new Maven Project
The first step in setup is to create a new Maven project. I will be using IntelliJ in this tutorial. The following steps need to be followed to create a new Maven project :
Open IntelliJ, Navigate to File >> New >> Project
2. In the New Project window, enter the following details:
Name of the Project – Cucumber_Playwright_JUnit5
Location/path where the project needs to be saved – Documents/Playwright (my location)
Select JDK version — I am using JDK 17
Archetype — Search for “quickstart” and select maven-archetype-quickstartfrom the results
Click on the Create button to create the project.
This will create a a new project in IntelliJ.
2. Set Up the Environment
2.1 Add the Cucumber, Playwright Java, and JUnit5 dependencies
Add the Cucumber, Playwright Java, and JUnit5 dependencies to the pom.xml. The latest dependency can be found here.
The compiler plugin is used to compile the source code of a Maven project. This plugin has two goals, which are already bound to specific phases of the default lifecycle:
Creating a well-organized project structure is crucial for maintaining a scalable and efficient automation framework.
1. Maven Configuration (pom.xml): This file manages project dependencies, plugins, and other configurations.
2. Features (src/test/resources/…/features): It contains Gherkin feature files written in plain English.
3. StepDefinition (src/test/java/.../tests/): Here, you write your actual test cases, utilizing the page classes. Cucumber steps are implemented.
4. Runner (src/main/java/…runner/): It connects Cucumber feature files with Junit Platform and triggers execution.
5. Hooks (src/test/java/…/hooks): Hooks manage test lifecycle events like Launch browser before scenario, Close browser after scenario, Capture screenshots on failure
6. Page Classes (src/main/java/.../pages/): Each web page in your application should have a corresponding page class. This class encapsulates all the functionalities of the web page, following the Page Object Model.
7. Steps (src/test/java/…/steps): It is an extra abstraction layer between step definitions and pages. It contains code that is common across scenarios.
8. Utility Classes (src/main/java/.../utils/): These classes can include various utilities like configuration readers, browser factory, helper methods for common tasks, etc.
9. junit-platform.properties: It contains global configuration for Junit Platform.
4. Create a feature file under src/test/resources/features
It is recommended to create a features folder in the src/test/resources directory. Create all the feature files in this features folder. Feature file should be saved as an extension of .feature. The test scenarios in the Feature file are written in Gherkins language. Add the test scenarios in this feature file. I have added sample test scenarios.
Feature: Login to HRM Application
Background:
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
@ValidCredentials
Scenario: Login with valid credentials
When User enters username as "Admin" and password as "admin123"
Then User should be able to login successfully and new page opens with heading "Dashboard"
@InvalidCredentials
Scenario Outline: Login with invalid credentials
When User enters username as "<username>" and password as "<password>"
Then User should be able to see error message "<errorMessage>"
Examples:
| username | password | errorMessage |
| Admin | admin12$$ | Invalid credentials |
| admin$$ | admin123 | Invalid credentials |
| abc123 | xyz$$ | Invalid credentials |
@MissingUsername
Scenario: Login with blank username
When User enters username as " " and password as "admin123"
Then User should be able to see a message "Required" below Username
5. Create the utility package in src/test/java
Create a Playwright Factory that includes methods to start and close the browser.
package com.example.utils;
import com.microsoft.playwright.*;
public class PlaywrightFactory {
private static Playwright playwright;
private static Browser browser;
private static BrowserContext context;
private static Page page;
public static void initBrowser() {
playwright = Playwright.create();
browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setHeadless(false));
context = browser.newContext();
page = context.newPage();
}
public static Page getPage() {
return page;
}
public static void closeBrowser() {
if (page != null) page.close();
if (context != null) context.close();
if (browser != null) browser.close();
if (playwright != null) playwright.close();
}
}
6. Create the hooks package in src/test/java
Create a Hook class that contains the code that runs before or after each scenario. Here, it is responsible for
Each web page in your application should have a corresponding page class. This class encapsulates all the functionalities of the web page, following the Page Object Model.
LoginPage
package com.example.pages;
import com.microsoft.playwright.*;
public class LoginPage {
private Page page;
// Locators
private final Locator usernameLocator;
private final Locator passwordLocator;
private final Locator submitLocator;
private final Locator invalidCredentialsLocator;
private final Locator missingUsernameLocator;
public LoginPage(Page page) {
this.page = page;
this.usernameLocator = page.locator("input[name='username']");
this.passwordLocator = page.locator("input[name='password']");
this.submitLocator = page.locator("button[type='submit']");
this.invalidCredentialsLocator = page.locator("//p[contains(@class, "oxd-text oxd-text--p oxd-alert-content-text")]");
this.missingUsernameLocator = page.locator("//span[contains(@class, 'oxd-text oxd-text--span oxd-input-field-error-message oxd-input-group__message')]");
}
public void login(String user, String pass){
usernameLocator.fill(user);
passwordLocator.fill(pass);
submitLocator.click();
}
public void getUrl(String url){
page.navigate(url);
}
public String getMissingUsernameErrorMessage() {
return missingUsernameLocator.textContent();
}
public String getErrorMessage () {
return invalidCredentialsLocator.textContent();
}
}
DashboardPage
package com.example.pages;
import com.microsoft.playwright.Locator;
import com.microsoft.playwright.Page;
public class DashboardPage {
private Page page;
private final Locator headingLocator;
public DashboardPage(Page page){
this.page = page;
this.headingLocator = page.locator("//h6[contains(@class, "oxd-topbar-header-breadcrumb-module")]");
}
public String getDashboardPageHeading() {
return headingLocator.textContent();
}
}
8. Create the step package in src/test/java
package com.example.steps;
import com.example.utils.PlaywrightFactory;
import com.microsoft.playwright.Page;
public class BaseStep {
protected Page getPage() {
Page page = PlaywrightFactory.getPage();
if (page == null) {
throw new RuntimeException(
"Page is NULL. Ensure @Before hook ran and glue includes hooks package."
);
}
return page;
}
}
9. Create the step definition class in src/test/java
Create the step definition class corresponding to the feature file to test the scenarios in the src/test/java directory. The StepDefinition files should be created in this definitionsdirectory within the folder called definitions.
Below is the step definition of the LoginPage feature file.
package com.example.definitions;
import com.example.pages.DashboardPage;
import com.example.pages.LoginPage;
import com.example.steps.BaseStep;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import org.junit.jupiter.api.Assertions;
public class LoginPageDefinitions extends BaseStep {
private LoginPage loginPage;
private DashboardPage dashboardPage;
@Given("User is on HRMLogin page {string}")
public void userIsOnHRMLoginPage(String baseUrl) {
loginPage = new LoginPage(getPage());
loginPage.getUrl(baseUrl);
}
@When("User enters username as {string} and password as {string}")
public void userEntersUsernameAsAndPasswordAs(String username, String password) {
loginPage.login(username, password);
}
@Then("User should be able to see error message {string}")
public void userShouldBeAbleToSeeErrorMessage(String expectedErrorMessage) {
Assertions.assertEquals(expectedErrorMessage, loginPage.getErrorMessage());
}
@Then("User should be able to see a message {string} below Username")
public void userShouldBeAbleToSeeAMessageBelowUsername(String expectedErrorMessage) {
Assertions.assertEquals(expectedErrorMessage, loginPage.getMissingUsernameErrorMessage());
}
@Then("User should be able to login successfully and new page opens with heading {string}")
public void userShouldBeAbleToLoginSuccessfullyAndNewPageOpen(String expectedHeading) {
dashboardPage = new DashboardPage(getPage());
Assertions.assertEquals(expectedHeading, dashboardPage.getDashboardPageHeading());
}
}
10. Create a JUnit Platform Cucumber Runner class in src/test/java
We need to create a class called Runner class to run the tests.
This JUnit 5 runner configures and launches Cucumber scenarios by selecting feature files, defining glue code packages, and executing them via the Cucumber engine on the JUnit Platform.
package com.example.runner;
import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME;
import org.junit.platform.suite.api.ConfigurationParameter;
import org.junit.platform.suite.api.IncludeEngines;
import org.junit.platform.suite.api.SelectClasspathResource;
import org.junit.platform.suite.api.Suite;
@Suite
@IncludeEngines("cucumber")
@SelectClasspathResource("features")
@ConfigurationParameter(
key = GLUE_PROPERTY_NAME, value = "com.example"
)
public class RunnerTests {
}
1. @Suite – Marks this class as a JUnit 5 test suite. Acts as the entry point for test execution
2. @IncludeEngines(“cucumber”) – Tells JUnit Platform to use the Cucumber engine
3. @SelectClasspathResource(“features”) – Specifies the location of feature file
4. @ConfigurationParameter – Tells Cucumber where to find step definitions and hooks
11. Run the test using the Junit Platform Runner File
Go to the Runner class and right-click and select Run ‘RunnerTests’. The tests will run in IntelliJ.
Below is the test execution result. 1 test is failed intentionally to show the status of test execution in the report.
12. Run the tests from the Command Line
Run the command below in the command prompt to run the tests and to get the test execution report.
mvn clean site test
The execution screen looks like something as shown below.
13. Cucumber Report Generation
Add junit-platform.properties under src/test/resources and add the below instructions in the file.
cucumber.publish.enabled=true
Below is the image of the Cucumber Report generated using the Cucumber Service.
We can see the details of the failed test in the Report.
The highlighted part holds the screenshot of the failed test.
14. Surefire Report Generation
Maven Site Plugin creates a folder – site under the target directory.
Junit also produces an “index.html” report, and it resides under the target folder. The below image shows the index.html report. This is the latest theme of the report.
Recap of Key Points:
Playwright and Java: We explored the powerful combination of Playwright with Java. We highlighted the benefits of this duo in automated testing.
Build: Maven manage the dependencies and execution.
Cucumber (BDD Framework):It enables Behavior-Driven Development (BDD).
Feature: It uses Gherkin feature file to create the scenarios.
StepDefinition: Stepdefinition file contains the test code related to the feature file. When Cucumber executes a Gherkin step in a scenario, it will look for a matching step definition to execute.
Test Runner: It acts as a bridge between the feature and StepDefinition file.
JUnit5: It controls the test execution lifecycle. It integrates with Cucumber via Junit Platform runner.
Hooks: Hooks manage browser setup, teardown, and failure handling
Playwright is integrated with Java, Cucumber, and TestNG. This integration creates a robust and scalable test automation framework for modern web applications. It is a BDD-driven framework.
Page Object Model (POM) is a design pattern used in test automation. It enhances test maintenance and reduces code duplication. In this pattern, web pages are represented as classes. The various elements on the page are defined as variables in the class. All possible user interactions can then be implemented as methods in the class.
What is Cucumber?
Cucumber is Behavior Driven Development (BDD). It is a methodology that integrates non-technical stakeholders, testers, and developers into the same conversation. This is achieved by using clearly defined specifications.
In behavior-driven development with Cucumber, test cases are written in Gherkin syntax. They use a Given-When-Then structure. This structure closely mirrors user stories and business rules.
Benefits of Using Cucumber with Playwright
1. Readable, business-friendly tests: Cucumber’s Gherkin syntax (Given–When–Then) makes test scenarios easy to understand for non-technical stakeholders, bridging the gap between QA, developers, and business teams.
2. Better Test Maintainability: With standardized step definitions and modularized test architecture, scaling up your test suite is effortless. Altering the suite won’t harm the entire framework.
3. Clear reporting and traceability: Cucumber’s reports map test results directly to feature files. They map directly to scenarios. This makes it easier to track coverage and failures against requirements.
4. Cross-browser and cross-platform coverage: When combined with Playwright, Cucumber scenarios can be executed across Chromium, Firefox, and WebKit without changing feature files
5. Scales well in CI/CD pipelines: The combination supports parallel execution. It offers tagging and selective test runs. This makes it suitable for continuous testing in CI environments.
What is TestNG?
TestNG is a testing framework widely used with Selenium for building structured, maintainable, and efficient automated tests. Combining TestNG listeners with Playwright gives testers greater control over test monitoring, logging, and custom actions based on test outcomes. In TestNG you can initialize Playwright and Browser in @BeforeClass method and destroy them in @AfterClass.
System requirements
The following prerequisites are required to be installed on the machine to begin with a smooth setup and installation.
Java 11 or higher
IntelliJ IDE or any other IDE to create a project
Maven
Browsers on which tests need to be run, like Chrome, Firefox, etc.
High-Level Architecture
Setting Up Cucumber BDD Framework with Playwright and TestNG
1. Create a new Maven Project
The first step in setup is to create a new Maven project. I will be using IntelliJ in this tutorial. The following steps need to be followed to create a new Maven project :
Open IntelliJ, Navigate to File >> New >> Project
In the New Project window, enter the following details:
Name of the Project – Cucumber_Playwright_TestNG
Location/path where the project needs to be saved – Documents/Playwright (my location)
Select JDK version — I am using JDK 17
Archetype — Search for “quickstart” and select maven-archetype-quickstartfrom the results
Click on the Create button to create the project.
This will create a project as shown in IntelliJ.
2. Set Up the Environment
2.1 Add the Cucumber, Playwright Java, and TestNG dependencies
Add the Cucumber, Playwright Java, and TestNG dependencies to the pom.xml. The latest dependency can be found here.
The compiler plugin is used to compile the source code of a Maven project. This plugin has two goals, which are already bound to specific phases of the default lifecycle:
Creating a well-organized project structure is crucial for maintaining a scalable and efficient automation framework.
1. Maven Configuration (pom.xml): This file manages project dependencies, plugins, and other configurations.
2. Features (src/test/resources/…/features): Contains Gherkin feature files written in plain English.
3. StepDefinition (src/test/java/.../tests/): Here, you write your actual test cases, utilizing the page classes. Cucumber stepsare implemented.
4. Runner (src/main/java/…runner/): It connects Cucumber feature files with TestNGand triggers execution.
5. Hooks (src/test/java/…/hooks): Hooks manage test lifecycle events like Launch browser before scenario, Close browser after scenario, Capture screenshots on failure
6. Page Classes (src/main/java/.../pages/): Each web page in your application should have a corresponding page class. This class encapsulates all the functionalities of the web page, following the Page Object Model.
7. Steps (src/test/java/…/steps): It is an extra abstraction layer between step definitions and pages. It contains code which is common across scenarios.
8. Utility Classes (src/main/java/.../utils/): These classes can include various utilities like configuration readers, browser factory, helper methods for common tasks, etc.
9. Test Data Files (src/test/resources/testdata/): Externalizes test data to avoid hardcoding. This includes items like usernames and passwords. These are stored in external files for easy management.
10. cucumber.properties: It contains global configuration for Cucumber.
11. testng.xml: This file is the central configuration file that defines how the test suite should be executed.
4. Create a feature file under src/test/resources
It is recommended to create a features folder in the src/test/resources directory. Create all the feature files in this features folder. Feature file should be saved as an extension of .feature. The test scenarios in the Feature file are written in Gherkins language. Add the test scenarios in this feature file. I have added sample test scenarios. Out of 5 tests, 4 will pass and 1 will fail.
Feature: Login to HRM Application
Background:
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
@ValidCredentials
Scenario: Login with valid credentials
When User enters username as "Admin" and password as "admin123"
Then User should be able to login successfully and new page opens with heading "Dashboard"
@InvalidCredentials
Scenario Outline: Login with invalid credentials
When User enters username as "<username>" and password as "<password>"
Then User should be able to see error message "<errorMessage>"
Examples:
| username | password | errorMessage |
| Admin | admin12$$ | Invalid credentials |
| admin$$ | admin123 | Invalid credentials |
| abc123 | xyz$$ | Invalid credentials |
@MissingUsername
Scenario: Login with blank username
When User enters username as " " and password as "admin123"
Then User should be able to see a message "Required1" below Username
5. Create the utility package in src/test/java
Create a Playwright Factory that contains the methods to start the browser and close the browser.
package com.example.utils;
import com.microsoft.playwright.*;
public class PlaywrightFactory {
private static Playwright playwright;
private static Browser browser;
private static BrowserContext context;
private static Page page;
public static void initBrowser() {
playwright = Playwright.create();
browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setHeadless(false));
context = browser.newContext();
page = context.newPage();
}
public static Page getPage() {
return page;
}
public static void closeBrowser() {
if (page != null) page.close();
if (context != null) context.close();
if (browser != null) browser.close();
if (playwright != null) playwright.close();
}
}
6. Create the hooks package in src/test/java
Create a Hook class that contain the code that runs before or after each scenario. Here, it is responsible for
Each web page in your application should have a corresponding page class. This class encapsulates all the functionalities of the web page, following the Page Object Model.
LoginPage
package com.example.pages;
import com.microsoft.playwright.*;
public class LoginPage {
private Page page;
// Locators
private final Locator usernameLocator;
private final Locator passwordLocator;
private final Locator submitLocator;
private final Locator invalidCredentialsLocator;
private final Locator missingUsernameLocator;
public LoginPage(Page page) {
this.page = page;
this.usernameLocator = page.locator("input[name='username']");
this.passwordLocator = page.locator("input[name='password']");
this.submitLocator = page.locator("button[type='submit']");
this.invalidCredentialsLocator = page.locator("//p[contains(@class, \"oxd-text oxd-text--p oxd-alert-content-text\")]");
this.missingUsernameLocator = page.locator("//span[contains(@class, 'oxd-text oxd-text--span oxd-input-field-error-message oxd-input-group__message')]");
}
public void login(String user, String pass){
usernameLocator.fill(user);
passwordLocator.fill(pass);
submitLocator.click();
}
public void getUrl(String url){
page.navigate(url);
}
public String getMissingUsernameErrorMessage() {
return missingUsernameLocator.textContent();
}
public String getErrorMessage () {
return invalidCredentialsLocator.textContent();
}
}
DashboardPage
package com.example.pages;
import com.microsoft.playwright.Locator;
import com.microsoft.playwright.Page;
public class DashboardPage {
private Page page;
private final Locator headingLocator;
public DashboardPage(Page page){
this.page = page;
this.headingLocator = page.locator("//h6[contains(@class, \"oxd-topbar-header-breadcrumb-module\")]");
}
public String getDashboardPageHeading() {
return headingLocator.textContent();
}
}
8. Create the step package in src/test/java
package com.example.steps;
import com.example.utils.PlaywrightFactory;
import com.microsoft.playwright.Page;
public class BaseStep {
protected Page getPage() {
Page page = PlaywrightFactory.getPage();
if (page == null) {
throw new RuntimeException(
"Page is NULL. Ensure @Before hook ran and glue includes hooks package."
);
}
return page;
}
}
9. Create the step definition class in src/test/java
Create the step definition class corresponding to the feature file to test the scenarios in the src/test/java directory. The StepDefinition files should be created in this definitionsdirectory within the folder called definitions.
Below is the step definition of the LoginPage feature file.
package com.example.definitions;
import com.example.steps.BaseStep;
import com.example.pages.DashboardPage;
import com.example.pages.LoginPage;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import org.testng.Assert;
public class LoginPageDefinitions extends BaseStep {
private LoginPage loginPage;
private DashboardPage dashboardPage;
@Given("User is on HRMLogin page {string}")
public void userIsOnHRMLoginPage(String baseUrl) {
loginPage = new LoginPage(getPage());
loginPage.getUrl(baseUrl);
}
@When("User enters username as {string} and password as {string}")
public void userEntersUsernameAsAndPasswordAs(String username, String password) {
loginPage.login(username, password);
}
@Then("User should be able to see error message {string}")
public void userShouldBeAbleToSeeErrorMessage(String expectedErrorMessage) {
Assert.assertEquals(loginPage.getErrorMessage(), expectedErrorMessage);
}
@Then("User should be able to see a message {string} below Username")
public void userShouldBeAbleToSeeAMessageBelowUsername(String expectedErrorMessage) {
Assert.assertEquals(loginPage.getMissingUsernameErrorMessage(), expectedErrorMessage);
}
@Then("User should be able to login successfully and new page opens with heading {string}")
public void userShouldBeAbleToLoginSuccessfullyAndNewPageOpen(String expectedHeading) {
dashboardPage = new DashboardPage(getPage());
Assert.assertEquals(dashboardPage.getDashboardPageHeading(), expectedHeading);
}
}
10. Create a TestNG Cucumber Runner class in src/test/java
We need to create a class called Runner class to run the tests. This class will use the TestNG annotation @RunWith(), which tells TestNG what is the test runner class. TestRunner should be created under src/test/java within the folder called runner.
AbstractTestNGCucumberTestsis a class provided by the Cucumber framework when we are integrating Cucumber with the TestNG test framework. This abstract class helps facilitate the execution of Cucumber tests using TestNG.
We need to specify the values of browser in the TestNG XML file that will pass to the test case file.
Let’s create a testng.xml file to run the test using TestNG.
Right click on the project, New >> File
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Playwright test suite" thread-count="3">
<test name="Chromium Tests">
<classes>
<class name="com.example.runner.RunnerTests"/>
</classes>
</test>
</suite>
12. Run the test through Runner Class
Go to the Runner class and right-click and select Run ‘RunnerTests’. The tests will run as TestNG tests (in IntelliJ).
Below is the test execution result.
13. Run the tests from TestNG.xml
Execute the tests by right-clicking on testng.xml and select Run ‘…testng.xml’
This will run the tests in chromium as mentioned in the .xml file.
The output of the above program is
14. Run the tests from the Command Line
Run the below command in the command prompt to run the tests and to get the test execution report.
mvn clean test
The execution screen looks like something as shown below.
15. Cucumber Report Generation
Add cucumber.properties under src/test/resources and add the below instructions in the file.
cucumber.publish.enabled=true
Below is the image of the Cucumber Report generated using the Cucumber Service.
The screenshot of the failed test is also attached in the Cucumber Report.
Click on the highlighted part. It contains the screenshot.
16. TestNG Report Generation
TestNG generates various types of reports under test-output folder like emailable-report.html, index.html, testng-results.xml.
We are interested in the emailable-report.html report. Open emailable-report.html, as this is an HTML report, and open it with the browser. The below image shows emailable-report.html.
TestNG also produces an “index.html” report, and it resides under the test-output folder. The below image shows the index.html report. This is the latest theme of the report.
Recap of Key Points:
Playwright and Java: We explored the powerful combination of Playwright with Java. We highlighted the benefits of this duo in automated testing.
Build: Maven manage the dependencies and execution.
Cucumber (BDD Framework):It enables Behavior-Driven Development (BDD).
Feature: It uses Gherkin feature file to create the scenarios.
StepDefinition: StepDefinition file contains the test code related to the feature file. When Cucumber executes a Gherkin step in a scenario, it will look for a matching step definition to execute.
Test Runner: It acts as a bridge between the feature and StepDefinition file.
TestNG: It controls the test execution lifecycle. It integrates with Cucumber via TestNG runner.
Hooks: Hooks manage browser setup, teardown, and failure handling
Large test suites can be structured to optimize ease of authoring and maintenance. Page object models are one such approach to structure the test suite. In this tutorial blog, we will learn to create and set up the project using Page Object Model in Playwright Java and JUnit5.
Page Object Model (POM) is a design pattern used in test automation. It enhances test maintenance and reduces code duplication. In this pattern, web pages are represented as classes. The various elements on the page are defined as variables in the class. All possible user interactions can then be implemented as methods in the class.
Why is Page Object Model Important?
1. Improved Test Maintenance: POM allows for easy updates to test scripts when the UI changes. The test logic is separated from the page definitions. This way, only the page objects need to be updated rather than all test scripts.
2. Maintainability: In this model, separate classes are created for different pages of a web application like login page, the home page, employee detail page, change password page, etc. So, if there is any change in any element of a website then we only need to make changes in one class, and not in all classes.
3. Reusability of Code: Shared page objects can be reused across multiple test cases, reducing redundancy. This ensures consistent interactions with web elements and actions, making the test code more efficient and easier to manage.
4. Facilitates Collaboration: A well-structured POM increases collaboration among teams. Each contributor can work on different parts of the application without interfering with one another. This approach helps in managing multiple test scenarios simultaneously.
5. Ease of Debugging: With POM, finding and fixing bugs becomes more straightforward since each page action is located in a specific place. Developers can easily pinpoint the location of a problem without sifting through complex test logic.
What is Playwright?
Page Object Model (POM) is a design pattern used in test automation. It enhances test maintenance and reduces code duplication. In this pattern, web pages are represented as classes. The various elements on the page are defined as variables in the class. All possible user interactions can then be implemented as methods in the class.
What is JUnit5?
JUnit5 is a test execution engine. It is used as testing framework for Java applications. It provides an easy-to-use platform for writing and executing tests. JUnit 5 is composed of three main modules: JUnit Platform, JUnit Jupiter and JUnit Vintage
System requirements
The following prerequisites are required to be installed on the machine to begin with a smooth setup and installation.
Java 11 or higher
IntelliJ IDE or any other IDE to create a project
Maven
Browsers on which tests need to be run, like Chrome, Firefox, etc.
Implementation Steps
1. Create a new Maven Project
The first step in setup is to create a new Maven project. I will be using IntelliJ in this tutorial. The following steps need to be followed to create a new Maven project :
Open IntelliJ, Navigate to File >> New >> Project
2. In the New Project window, enter the following details:
Name of the Project – PageObjectModel_Playwright_JUnit5
Location/path where the project needs to be saved – Documents/Playwright (my location)
Select JDK version — I am using JDK 17
Archetype — Search for “quickstart” and select maven-archetype-quickstart from the results
Click on the Create button to create the project.
This will create a project as shown below in the IntelliJ.
2. Setup Playwright with Java and JUnit5
Add the Playwright and JUnit5 dependencies to the pom.xml. The latest dependency can be found from here.
After adding the dependency, refresh the project. We will see that the Playwright jar files are downloaded in External Libraries.
3. Project Structure for Maintainability
Creating a well-organized project structure is crucial for maintaining a scalable and efficient automation framework.
Page Classes (src/main/java/.../pages/): Each web page in your application should have a corresponding page class. This class encapsulates all the functionalities of the web page, following the Page Object Model.
Utility Classes (src/main/java/.../utils/): These classes can include various utilities like configuration readers, helper methods for common tasks, etc.
Test Classes (src/test/java/.../tests/): Here, you write your actual test cases, utilizing the page classes.
Test Data Files (src/test/resources/testdata/): Keep your test data, like usernames and passwords, in external files for easy management and to avoid hardcoding.
Maven Configuration (pom.xml): This file manages project dependencies, plugins, and other configurations.
4. Creating Page Object Classes
Page classes serve as an interface to a web page’s UI elements. Each page class corresponds to a page in your application, encapsulating the actions that can be performed on that page.
Identify the UI Elements: Determine all the elements on the web page that your test will interact with, such as text boxes, buttons, and links.
Define Selectors:Store the selectors for these elements in your page class. It’s a best practice to externalize these selectors, but for simplicity, we’ll define them directly in the class.
Implement Actions:Create methods for each action that can be performed on the page, like entering text, clicking buttons, etc.
Creating LoginPage Class
import com.microsoft.playwright.Page;
public class LoginPage {
private Page page;
//Locators
private final String usernameLocator = "input[name='username']";
private final String passwordLocator = "input[name='password']";
private final String submitButton = "button[type='submit']";
private final String errorMessage = "//p[contains(@class, 'oxd-text oxd-text--p oxd-alert-content-text')]";
//Constructor
public LoginPage(Page page){
this.page = page;
}
public void login(String username, String password) {
page.fill(usernameLocator,username);
page.fill(passwordLocator,password);
page.click(submitButton);
}
public String getErrorMessage(){
return page.textContent(errorMessage);
}
}
Creating DashboardPage Class
package org.example.pages;
import com.microsoft.playwright.Page;
public class DashboardPage {
private Page page;
//Locators
private final String dashboardHeading = "//h6[contains(@class, 'oxd-topbar-header-breadcrumb-module')]";
//Constructor
public DashboardPage(Page page) {
this.page = page;
}
// Methods
public String getHeading() {
return page.textContent(dashboardHeading);
}
}
5. Write the Test Scripts
Create a Test files under src/test/java. Use these page classes in your test scripts to perform end-to-end scenarios. This will keep your tests clean and focused on logic rather than details about the UI elements.
A typical test class includes:
Setup Method: Initializes the Playwright browser and other prerequisites before each test.
Test Methods: Individual test cases, each representing a different scenario.
Teardown Method: Closes the browser and performs any cleanup after each test.
Creating BaseTests
This class contains the common variables and methods used throughout the project, like setup and teardown methods.
package org.example.utils;
package com.example.utils;
import com.microsoft.playwright.*;
import org.junit.jupiter.api.*;
// Subclasses will inherit PER_CLASS behavior.
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class BaseTests {
// Shared between all tests in the class.
static Playwright playwright;
static Browser browser;
// New instance for each test method.
BrowserContext context;
protected Page page;
@BeforeAll
public static void launchBrowser() {
playwright = Playwright.create();
browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setHeadless(false));
}
@AfterAll
public static void closeBrowser() {
playwright.close();
}
@BeforeEach
public void createContextAndPage() {
context = browser.newContext();
page = context.newPage();
page.navigate("https://opensource-demo.orangehrmlive.com/web/index.php/auth/login");
}
@AfterEach
public void closeContext() {
context.close();
}
}
Explanation
In JUnit you can initialize Playwright and Browser in @BeforeAll method and destroy them in @AfterAll. Use @TestInstance(TestInstance.Lifecycle.PER_CLASS) annotation to make JUnit create one instance of a class for all test methods within that class (by default each JUnit will create a new instance of the class for each test method). Store Playwright and Browser objects in instance fields. They will be shared between tests. Each instance of the class will use its own copy of Playwright.
1. Playwright playwright:
Initialized to manage the browser automation session, allowing interactions with different browsers.
2. Browser browser:
Declared to store the browser instance that will be shared across test methods within the class.
Playwright playwright;
Browser browser = null;
3. BrowserContext context:
Created anew for each test method to simulate independent browser sessions. This ensures that the browser state doesn’t carry over between tests.
4. Page page:
Represents a single tab or window in a browser used for performing actions and assertions.
BrowserContext context;
Page page;
5. @BeforeAll launchBrowser():
This method is annotated with @BeforeAll, indicating it runs once before any test methods in the current class. The browser is launched in non-headless mode (setHeadless(false)), meaning an actual browser window is opened.
This method runs before each test method in the class, as indicated by the @BeforeEach annotation. It creates a new BrowserContext and a Page, ensuring each test runs in a clean, isolated state. The method also navigates to a login page (“https://opensource-demo.orangehrmlive.com/web/index.php/auth/login”), setting up the initial state for each test.
Runs after each test method, annotated with @AfterMethod. Closes the BrowserContext, effectively closing the browser tab and cleaning up resources.
@AfterAll
public static void closeBrowser() {
playwright.close();
}
8. @AfterEach closeContext()
Runs once after all test methods in the current class have executed, as indicated by the @AfterEach annotation. Closes the Playwright instance, freeing up all resources used for automation.
@AfterEach
public void closeContext() {
context.close();
}
Creating a Login Page Test Class:
We will be automating the following test scenario using Playwright Java and run them in Chromium
Verify the invalid username generates error message “Invalid credentials”
package com.example.tests;
import com.example.pages.DashboardPage;
import com.example.pages.LoginPage;
import com.example.utils.BaseTests;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class LoginTests extends BaseTests {
@Test
public void unsuccessfulLogin() {
LoginPage loginPage = new LoginPage(page);
loginPage.login("abc","abc");
String actualErrorMessage = loginPage.getErrorMessage();
Assertions.assertEquals("Invalid credentials", actualErrorMessage, "");
}
@Test
public void successfulLogin() {
LoginPage loginPage = new LoginPage(page);
loginPage.login("Admin","admin123");
DashboardPage dashboardPage = new DashboardPage(page);
String actualHeading = dashboardPage.getDashboardPageHeading();
Assertions.assertEquals("Dashboard",actualHeading, "Unable to login to the application");
}
}
Explanation
1. successfulLogin
This method enters correct credentials and login to the application.
2. unsuccessfulLogin
This method enter the invalid username and password and click on the Submit button. Then, it assert that the error message generated by the application is equal to “Invalid credentials”.
7. Test Execution through IntelliJ
Go to the Test class and right-click and select Run ‘LoginTests’. The tests will run as JUnit5 tests (in IntelliJ).
Below is the test execution result.
8. Run the tests using the command line
Use the below command to run the tests using the command line.
mvn clean test site
This will run the tests in chromium as mentioned in the .xml file.
Chromium
The output of the above program is
9. JUnit5 Report Generation
Maven Site Plugin creates a folder – site under the target directory.
Right-click on the Junit5 Report.html and open it in the browser.
Recap of Key Points:
Playwright and Java: We explored the powerful combination of Playwright with Java, highlighting the benefits of this duo in automated testing.
Project Structure: An optimal project layout was discussed, emphasizing maintainability and organization.
Page Object Model (POM): The implementation of POM was outlined, showcasing its importance in creating scalable and maintainable test scripts.
Page Classes:We explored the creation of page classes. They are a core component of POM. We provided examples to illustrate their structure. The examples also showed their functionality.
Test Classes:The structure and best practices for writing test classes were covered. This ensures clarity. It also enhances efficiency in your tests.
Large test suites can be structured to optimize ease of authoring and maintenance. Page object models are one such approach to structure the test suite. In this tutorial blog, we will learn to create and set up the project using Page Object Model in Playwright Java and TestNG.
Page Object Model (POM) is a design pattern used in test automation that enhances test maintenance and reduces code duplication where web pages are represented as classes, and the various elements on the page are defined as variables in the class and all possible user interactions can then be implemented as methods in the class.
Why is Page Object Model Important?
1. Improved Test Maintenance: POM allows for easy updates to test scripts when the UI changes. By separating the test logic from the page definitions, only the page objects need to be updated rather than all test scripts.
2. Maintainability: In this model, separate classes are created for different pages of a web application like login page, the home page, employee detail page, change password page, etc. So, if there is any change in any element of a website then we only need to make changes in one class, and not in all classes.
3. Reusability of Code: Shared page objects can be reused across multiple test cases, reducing redundancy. This ensures consistent interactions with web elements and actions, making the test code more efficient and easier to manage.
4. Facilitates Collaboration: A well-structured POM increases collaboration among teams, as each contributor can work on different parts of the application without interfering with one another. This approach helps in managing multiple test scenarios simultaneously.
5. Ease of Debugging: With POM, finding and fixing bugs becomes more straightforward since each page action is located in a specific place. Developers can easily pinpoint the location of a problem without sifting through complex test logic.
System requirements
The following prerequisites are required to be installed on the machine to begin with a smooth setup and installation.
Java 11 or higher
IntelliJ IDE or any other IDE to create a project
Maven
Browsers on which tests need to be run, like Chrome, Firefox, etc.
Implementation Steps
1. Create a new Maven Project
The first step in setup is to create a new Maven project. I will be using IntelliJ in this tutorial. The following steps need to be followed to create a new Maven project :
Open IntelliJ, Navigate to File >> New >> Project
2. In the New Project window, enter the following details:
Name of the Project – Playwright_TestNG_Demo
Location/path where the project needs to be saved – Documents/Playwright (my location)
Select JDK version — I am using JDK 17
Archetype — Search for “quickstart” and select maven-archetype-quickstart from the results
Click on the Create button to create the project.
This will create a project as shown below in the IntelliJ.
2. Set Up the Environment
Add the Playwright and TestNG dependencies to the pom.xml. The latest dependency can be found here.
After adding the dependency, refresh the project. We will see that the Playwright jar files are downloaded in External Libraries.
3. Project Structure for Maintainability
Creating a well-organized project structure is crucial for maintaining a scalable and efficient automation framework.
1. Page Classes (src/main/java/.../pages/): Each web page in your application should have a corresponding page class. This class encapsulates all the functionalities of the web page, following the Page Object Model.
2. Utility Classes (src/main/java/.../utils/): These classes can include various utilities like configuration readers, helper methods for common tasks, etc.
3. Test Classes (src/test/java/.../tests/): Here, you write your actual test cases, utilizing the page classes.
4. Test Data Files (src/test/resources/testdata/): Keep your test data in external files. This ensures easy management. It also helps to avoid hardcoding usernames and passwords.
5. Maven Configuration (pom.xml): This file manages project dependencies, plugins, and other configurations.
6. testng.xml: This file is the central configuration file that defines how the test suite should be executed.
4. Creating Page Object Classes
Page classes serve as an interface to a web page’s UI elements. Each page class corresponds to a page in your application, encapsulating the actions that can be performed on that page.
Identify the UI Elements: Determine all the elements on the web page that your test will interact with, such as text boxes, buttons, and links.
Define Selectors: Store the selectors for these elements in your page class. It’s a best practice to externalize these selectors, but for simplicity, we’ll define them directly in the class.
Implement Actions:Create methods for each action that can be performed on the page, like entering text, clicking buttons, etc.
Creating LoginPage Class
import com.microsoft.playwright.Page;
public class LoginPage {
private Page page;
//Locators
private final String usernameLocator = "input[name='username']";
private final String passwordLocator = "input[name='password']";
private final String submitButton = "button[type='submit']";
private final String errorMessage = "//p[contains(@class, 'oxd-text oxd-text--p oxd-alert-content-text')]";
//Constructor
public LoginPage(Page page){
this.page = page;
}
public void login(String username, String password) {
page.fill(usernameLocator,username);
page.fill(passwordLocator,password);
page.click(submitButton);
}
public String getErrorMessage(){
return page.textContent(errorMessage);
}
}
Creating DashboardPage Class
package org.example.pages;
import com.microsoft.playwright.Page;
public class DashboardPage {
private Page page;
//Locators
private final String dashboardHeading = "//h6[contains(@class, 'oxd-topbar-header-breadcrumb-module')]";
//Constructor
public DashboardPage(Page page) {
this.page = page;
}
// Methods
public String getHeading() {
return page.textContent(dashboardHeading);
}
}
5. Write the Test Scripts
Create a Test files under src/test/java. Use these page classes in your test scripts to perform end-to-end scenarios. This will keep your tests clean and focused on logic rather than details about the UI elements.
A typical test class includes:
Setup Method: Initializes the Playwright browser and other prerequisites before each test.
Test Methods: Individual test cases, each representing a different scenario.
Teardown Method:Closes the browser and performs any cleanup after each test.
We will be automating the following test scenario using Playwright Java and run them in Chromium.
Verify the invalid username generates error message “Invalid credentials”
Creating BaseClass
This class contains the common variables and methods used throughout the project, like setup and teardown methods.
package org.example.utils;
import com.microsoft.playwright.*;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
public class BaseTests {
// Shared between all tests in this class.
protected Playwright playwright;
protected Browser browser;
// New instance for each test method.
protected BrowserContext context;
protected Page page;
@BeforeClass
public void launchBrowser() {
playwright = Playwright.create();
browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setHeadless(false));
}
@BeforeMethod
public void createContextAndPage() {
context = browser.newContext();
page = context.newPage();
page.navigate("https://opensource-demo.orangehrmlive.com/web/index.php/auth/login");
}
@AfterMethod
public void closeContext() {
context.close();
}
@AfterClass
public void closeBrowser() {
playwright.close();
}
}
Explanation
1. Playwright playwright:
Initialized to manage the browser automation session, allowing interactions with different browsers.
2. Browser browser:
Declared to store the browser instance that will be shared across test methods within the class.
Playwright playwright;
Browser browser = null;
3. BrowserContext context:
Created anew for each test method to simulate independent browser sessions. This ensures that the browser state doesn’t carry over between tests.
4. Page page:
Represents a single tab or window in a browser used for performing actions and assertions.
BrowserContext context;
Page page;
5. @BeforeClass:
This method is annotated with @BeforeClass, indicating it runs once before any test methods in the current class. The browser is launched in non-headless mode (setHeadless(false)), meaning an actual browser window is opened.
This method runs before each test method in the class, as indicated by the @BeforeMethod annotation. It creates a new BrowserContext and a Page, ensuring each test runs in a clean, isolated state. The method also navigates to a login page (“https://opensource-demo.orangehrmlive.com/web/index.php/auth/login”), setting up the initial state for each test.
Runs once after all test methods in the current class have executed, as indicated by the @AfterClass annotation. Closes the Playwright instance, freeing up all resources used for automation.
package org.example.tests;
import org.example.pages.DashboardPage;
import org.example.pages.LoginPage;
import org.example.utils.BaseTests;
import org.testng.Assert;
import org.testng.annotations.Test;
public class LoginTests extends BaseTests {
@Test
public void loginToApplication(){
LoginPage loginPage = new LoginPage(page);
loginPage.login("Admin","admin123");
DashboardPage dashboardPage = new DashboardPage(page);
String actualHeading = dashboardPage.getHeading();
Assert.assertEquals(actualHeading,"Dashboard", "Unable to login to the application");
}
@Test
public void invalidCredentials() {
LoginPage loginPage = new LoginPage(page);
loginPage.login("Playwright","test");
Assert.assertEquals(loginPage.getErrorMessage(), "Invalid credentials" ,"Incorrect Error Message is displayed");
}
}
Explanation
1. loginToApplication
This method enters correct credentials and login to the application.
2. verifyInvalidCredentials
This method enter the invalid username and password and click on the Submit button. Then, it assert that the error message generated by the application is equal to “Invalid credentials”.
6. Create testng.xml
We need to specify the values of browser in the TestNG XML file that will pass to the test case file. Let’s create a testng.xml file to run the test using TestNG.
Right click on the project, New >> File
Below is the sample testng.xml file.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Page Object Model Playwright test suite">
<test name="Chrome Test">
<parameter name="browser" value="chromium" />
<classes>
<class name="org.example.tests.LoginTests"/>
</classes>
</test>
</suite>
7. Test Execution through IntelliJ
Go to the Test class and right-click and select Run ‘LoginTests’. The tests will run as TestNG tests (in IntelliJ).
Below is the test execution result.
8. Run the tests from TestNG.xml
Execute the tests by right-clicking on testng.xml and select Run ‘…testng.xml’
This will run the tests in chromium as mentioned in the .xml file.
The output of the above program is
9. TestNG Report Generation
TestNG generates various types of reports under test-output folder like emailable-report.html, index.html, testng-results.xml.
We are interested in the “emailable-report.html” report. Open “emailable-report.html“, as this is an HTML report, and open it with the browser. The below image shows emailable-report.html.
TestNG also produces an “index.html” report, and it resides under the test-output folder. The below image shows the index.html report. This is the latest theme of the report.
Recap of Key Points:
Playwright and Java:We explored the powerful combination of Playwright with Java. We highlighted the benefits of this duo in automated testing.
Project Structure:An optimal project layout was discussed, emphasizing maintainability and organization.
Page Object Model (POM): The implementation of POM was outlined, showcasing its importance in creating scalable and maintainable test scripts.
Page Classes:We explored how to create page classes, which are a core component of POM. We also provided examples to illustrate their structure. These examples demonstrate their functionality.
Test Classes:We covered the structure and best practices for writing effective test classes. This ensures clarity and efficiency in your tests.
We know that Playwright runs the tests in non-headed mode. This means we can’t see the test opening the web page and performing various actions on it. But, we can run the tests in headed mode. This is useful for debugging or observing the actions taken by the test.
System requirements
The following prerequisites are required to be installed on the machine to begin with a smooth setup and installation.
Java 8 or higher
IntelliJ IDE or any other IDE to create a project
Maven
Browsers on which tests need to be run, like Chrome, Firefox, etc.
To run the tests in headed mode, use the below command:-
Below method is used to initialize a new Playwright instance. It is placed inside a try-with-resources block. This ensures that resources are closed automatically after use.
(Playwright playwright = Playwright.create())
2. Launch Web Browser in Visible mode
A new Chromium browser instance is launched in visible mode. The BrowserType.LaunchOptions class contains various settings that define how the browser should be started. The setHeadless(false) call is a method on the LaunchOptionsobject. Setting headlessto falsecauses the browser to launch in visible mode. This allows us to see the browser window as the automation runs.
The script pauses execution for 2000 milliseconds (or 2 seconds) using `page.waitForTimeout(2000)`, allowing time for the page to fully load. This is often used in testing to manage asynchronous loading of elements.
page.waitForTimeout(2000);
6. Print the title
The title of the page is retrieved and printed to the console using page.title().
System.out.println("Title :" + page.title());
7. Assert the page title
Below line asserts that the page has the title matching the regular expression “OrangeHRM”. The use of Pattern.compile() allows for matching with a regular expression, which can be beneficial for flexible title checking.
The try-with-resources structure ensures the browser and Playwright instance close automatically after the test completes.
The test execution in Playwright is very fast. Therefore, I have used the waitForTimeout() method to slow down the execution for 2 seconds. This allows for taking a print screen of the page. It opens the website as shown below.