Sunday, 12 July 2026

Playwright Data-Driven Testing with TestNG and Excel Using Java (Complete Step-by-Step Guide)

In real-world automation projects, hardcoding test data inside test scripts makes maintenance difficult. As applications evolve, test data changes frequently, and updating Java code for every change is inefficient.

Data-driven testing solves this problem by separating test logic from test data. Instead of embedding usernames, passwords, and other values directly in your test methods, you store them in external sources such as Excel files, CSV files, or databases.

In this tutorial, you'll learn how to build a data-driven Playwright framework using Java, TestNG, and Excel.


What is Data-Driven Testing?

Data-driven testing is a testing approach where the same test executes multiple times using different sets of input data.

For example, instead of writing separate login tests:

  • Login with Admin
  • Login with Manager
  • Login with Employee

You write one test and supply different credentials from an Excel sheet.

Benefits include:

  • Reduced code duplication
  • Easier maintenance
  • Better test coverage
  • Separation of test logic and data
  • Improved scalability

Project Structure

A clean folder structure helps keep the framework organized.

playwright-framework
│
├── src
│   ├── main
│   │    ├── pages
│   │    ├── utils
│   │    └── base
│   │
│   └── test
│        ├── tests
│        ├── resources
│        │     └── testdata
│        │            └── LoginData.xlsx
│        └── dataproviders
│
├── pom.xml
└── testng.xml

Step 1: Add Apache POI Dependencies

Apache POI is the most commonly used Java library for reading Excel files.

Add these dependencies to your pom.xml.

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.4.1</version>
</dependency>

Refresh your Maven project after saving the file.


Step 2: Create an Excel File

Create an Excel file named LoginData.xlsx.

Example:

UsernamePassword
adminAdmin@123
managerManager@123
employeeEmployee@123

Store the file under:

src/test/resources/testdata

Step 3: Create an Excel Utility Class

Use Apache POI to read Excel data.

public class ExcelUtils {

    public static Object[][] getLoginData() throws Exception {

        FileInputStream fis =
                new FileInputStream(
                "src/test/resources/testdata/LoginData.xlsx");

        Workbook workbook =
                WorkbookFactory.create(fis);

        Sheet sheet =
                workbook.getSheetAt(0);

        int rows = sheet.getPhysicalNumberOfRows();

        int columns =
                sheet.getRow(0).getLastCellNum();

        Object[][] data =
                new Object[rows - 1][columns];

        for (int i = 1; i < rows; i++) {

            for (int j = 0; j < columns; j++) {

                data[i - 1][j] =
                        sheet.getRow(i)
                             .getCell(j)
                             .toString();
            }
        }

        workbook.close();
        fis.close();

        return data;
    }
}

This utility reads the Excel file and returns a two-dimensional array suitable for TestNG.


Step 4: Create a DataProvider

Create a TestNG DataProvider.

@DataProvider(name = "loginData")

public Object[][] loginData()
throws Exception {

    return ExcelUtils.getLoginData();
}

The DataProvider supplies data to your test methods.


Step 5: Create a Login Page

Example Page Object.

public class LoginPage {

    private final Page page;

    public LoginPage(Page page) {

        this.page = page;
    }

    public void login(
            String username,
            String password) {

        page.fill("#username", username);

        page.fill("#password", password);

        page.click("#loginButton");
    }
}

Step 6: Create the Test Class

Use the DataProvider.

public class LoginTest extends BaseTest {

    @Test(dataProvider = "loginData",
          dataProviderClass =
          LoginDataProvider.class)

    public void verifyLogin(
            String username,
            String password) {

        page.navigate(
            "https://example.com/login");

        LoginPage login =
                new LoginPage(page);

        login.login(
                username,
                password);

        assertThat(page)
                .hasURL(
                "https://example.com/dashboard");
    }
}

TestNG executes the same test once for every row in the Excel sheet.


Execution Flow

Suppose your Excel contains three rows.

UsernamePassword
adminAdmin@123
managerManager@123
employeeEmployee@123

Execution:

Run 1 → admin

Run 2 → manager

Run 3 → employee

Only one test method is required.


Handling Different Data Types

Excel may contain:

  • Strings
  • Numbers
  • Dates
  • Booleans

Use Apache POI's DataFormatter to safely convert values.

Example:

DataFormatter formatter =
        new DataFormatter();

String value =
        formatter.formatCellValue(cell);

This preserves the displayed cell value and avoids formatting issues.


Parameterizing Different Test Scenarios

Data-driven testing isn't limited to login.

You can store:

  • Search keywords
  • Product IDs
  • Customer details
  • Payment information
  • API request data
  • Form inputs

The same approach can drive a wide variety of tests.


Best Practices

Keep Test Data Separate

Store test data outside Java classes.

Recommended locations:

  • Excel
  • JSON
  • CSV
  • YAML
  • Database

Avoid Duplicate Data

Use reusable datasets instead of copying values across multiple files.


Name Worksheets Clearly

Good examples:

  • LoginData
  • SearchData
  • PaymentData

Avoid generic names such as:

  • Sheet1
  • Test

Close Resources

Always close:

  • Workbook
  • FileInputStream

This prevents memory leaks and file locking issues.


Validate Expected Results

Don't verify only that a test runs.

Validate:

  • Successful login
  • Error messages
  • Dashboard visibility
  • Business rules

Common Interview Questions

What is Data-Driven Testing?

A testing approach where one test executes multiple times using different datasets.


Which library is commonly used to read Excel in Java?

Apache POI.


What is the purpose of TestNG's DataProvider?

It supplies multiple sets of data to a single test method, allowing the same test to run repeatedly with different inputs.


Can Playwright execute data-driven tests in parallel?

Yes. TestNG DataProviders can be combined with parallel execution, provided that browser sessions and test data remain thread-safe.


When should you use Excel instead of hardcoded values?

Excel is useful when test data changes frequently, is maintained by non-developers, or must support many test combinations. For API payloads or structured data, JSON may be a better choice.


Conclusion

Data-driven testing is a key technique for building scalable automation frameworks.

By combining Playwright, Java, TestNG, and Apache POI, you can separate test logic from test data, improve maintainability, and increase test coverage without duplicating code.

In this tutorial, you learned how to:

  • Read Excel data using Apache POI
  • Create a TestNG DataProvider
  • Execute Playwright tests with multiple datasets
  • Organize a maintainable framework
  • Follow best practices for enterprise automation

Once you've mastered data-driven testing, you'll be well prepared to build automation frameworks used in large enterprise applications.

Friday, 10 July 2026

Playwright Parallel Execution with TestNG Using Java (Complete Step-by-Step Guide)

As automation test suites grow, execution time also increases. Running hundreds of tests sequentially can take hours, slowing down development and delaying feedback.

Parallel execution solves this problem by running multiple tests simultaneously. Combined with Playwright's modern browser automation capabilities and TestNG's parallel execution features, you can significantly reduce execution time while maintaining reliable and scalable test automation.

In this guide, you'll learn how to configure parallel execution in Playwright using Java and TestNG.


Why Run Tests in Parallel?

Parallel execution offers several advantages:

  • Faster test execution
  • Reduced CI/CD pipeline duration
  • Better utilization of system resources
  • Quicker feedback for developers
  • Improved productivity for QA teams

For example:

Number of Tests Sequential Execution Parallel Execution (4 Threads)
100100 minutes25–30 minutes
500500 minutes130–150 minutes

The actual improvement depends on your application, hardware, and test design.


Understanding Thread Safety

Each test running in parallel must use its own isolated browser session.

Avoid sharing these objects across threads:

  • Playwright
  • BrowserContext
  • Page

Each test should create its own browser context to prevent data leakage and flaky tests.


BaseTest Class

Create browser resources for every test.

public class BaseTest {

    protected Playwright playwright;
    protected Browser browser;
    protected BrowserContext context;
    protected Page page;

    @BeforeMethod
    public void setup() {

        playwright = Playwright.create();

        browser = playwright.chromium()
                .launch(new BrowserType.LaunchOptions()
                        .setHeadless(true));

        context = browser.newContext();

        page = context.newPage();
    }

    @AfterMethod(alwaysRun = true)
    public void tearDown() {

        context.close();
        browser.close();
        playwright.close();
    }
}

Each test receives its own independent browser environment.


Creating Test Classes

Example:

public class LoginTest extends BaseTest {

    @Test
    public void verifyLogin() {

        page.navigate("https://example.com");

        page.locator("#username")
                .fill("admin");

        page.locator("#password")
                .fill("password");

        page.locator("#login")
                .click();
    }
}

You can create multiple test classes following the same pattern.


Configure TestNG for Parallel Execution

Update your testng.xml.

<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">

<suite name="Playwright Suite"
       parallel="methods"
       thread-count="4">

    <test name="Regression Tests">

        <classes>

            <class name="tests.LoginTest"/>
            <class name="tests.SearchTest"/>
            <class name="tests.CartTest"/>

        </classes>

    </test>

</suite>

In this configuration:

  • parallel="methods" runs test methods simultaneously.
  • thread-count="4" allows up to four concurrent threads.

Parallel Execution Options

TestNG supports multiple execution modes.

Methods

Runs individual test methods in parallel.

parallel="methods"

Best for independent tests.


Classes

Runs entire classes in parallel.

parallel="classes"

Each class gets its own thread.


Tests

Runs <test> sections in parallel.

parallel="tests"

Useful for separating smoke, regression, or browser-specific suites.


Instances

Runs different object instances simultaneously.

parallel="instances"

Typically used with data-driven testing.


Running Tests on Multiple Browsers

Parameterize your tests to execute on different browsers.

Example:

@Parameters("browser")
@BeforeMethod
public void setup(String browserName) {

    playwright = Playwright.create();

    if ("chromium".equalsIgnoreCase(browserName)) {

        browser = playwright.chromium().launch();

    } else if ("firefox".equalsIgnoreCase(browserName)) {

        browser = playwright.firefox().launch();

    } else {

        browser = playwright.webkit().launch();
    }

    context = browser.newContext();

    page = context.newPage();
}

This enables cross-browser execution with minimal code changes.


Common Challenges

Shared Test Data

Avoid using the same user account across parallel tests.

Instead:

  • Create unique test users.
  • Generate dynamic test data.
  • Clean up data after execution.

Shared Files

Avoid writing to the same file from multiple threads.

Instead:

  • Create thread-specific filenames.
  • Use timestamp-based naming.

Example:

Report_Thread1.html
Report_Thread2.html

Browser Context Isolation

Always create a new BrowserContext for each test.

context = browser.newContext();

Never reuse contexts between threads.


Best Practices

Keep Tests Independent

Each test should execute successfully regardless of execution order.


Avoid Static Variables

Static objects may be shared across threads, leading to unpredictable behavior.


Use Explicit Assertions

Verify business outcomes such as:

  • Successful login
  • Order creation
  • Payment confirmation

Avoid relying solely on page titles or URLs.


Capture Screenshots on Failure

Integrate screenshots into your reporting solution (such as Allure) for easier debugging.


Monitor Resource Usage

Parallel execution increases CPU and memory usage.

Choose a thread count appropriate for your machine and CI/CD agents.


Performance Tips

  • Run headless in CI/CD environments.
  • Reuse browser binaries instead of reinstalling them.
  • Execute smoke tests before full regression suites.
  • Use separate environments when running large parallel suites.

Common Interview Questions

Can Playwright execute tests in parallel?

Yes. Playwright supports parallel execution, and when combined with TestNG, multiple tests can run simultaneously.


Why should each test use its own BrowserContext?

A BrowserContext isolates cookies, storage, and session data. Separate contexts prevent interference between parallel tests.


What happens if Page objects are shared across threads?

Tests may overwrite each other's actions, causing flaky failures and inconsistent results.


Which TestNG parallel mode is most commonly used?

parallel="methods" is widely used because it provides good scalability for independent test cases.


Conclusion

Parallel execution is one of the most effective ways to reduce automation execution time and improve feedback cycles.

In this guide, you learned how to:

  • Configure parallel execution with TestNG
  • Build thread-safe Playwright tests
  • Execute tests across multiple browsers
  • Avoid common concurrency issues
  • Apply enterprise best practices

By combining Playwright, Java, and TestNG, you can create fast, scalable, and reliable automation frameworks that meet the demands of modern software development and CI/CD pipelines.

Thursday, 9 July 2026

Playwright Reporting with Allure Reports Using Java (Complete Step-by-Step Guide)

Automation testing is not just about executing test cases—it's also about presenting the results in a clear and meaningful way. While TestNG provides basic HTML reports, enterprise teams often use Allure Reports because they offer interactive dashboards, screenshots, execution history, attachments, and detailed failure analysis.

In this guide, you'll learn how to integrate Allure Reports with Playwright using Java and TestNG.


Why Use Allure Reports?

Allure Reports provide:

  • Interactive HTML reports
  • Test execution history
  • Test categorization
  • Screenshots for failed tests
  • Video attachments
  • Environment information
  • Timeline view
  • Easy CI/CD integration

Compared to standard TestNG reports, Allure makes debugging faster and reporting more professional.


Prerequisites

Before starting, ensure you have:

  • Java 17 or later
  • Maven installed
  • Playwright project
  • TestNG framework
  • Browser binaries installed

Step 1: Add Maven Dependencies

Add the following dependencies to your pom.xml.

<dependencies>

    <dependency>
        <groupId>io.qameta.allure</groupId>
        <artifactId>allure-testng</artifactId>
        <version>2.30.0</version>
    </dependency>

    <dependency>
        <groupId>com.microsoft.playwright</groupId>
        <artifactId>playwright</artifactId>
        <version>1.55.0</version>
    </dependency>

</dependencies>

Refresh your Maven project after saving the file.


Step 2: Configure Maven Surefire Plugin

Add the Surefire plugin to enable TestNG execution.

<build>

<plugins>

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-surefire-plugin</artifactId>

<version>3.5.3</version>

</plugin>

</plugins>

</build>

Step 3: Execute the Tests

Run your TestNG suite.

mvn clean test

During execution, Allure stores raw result files inside:

allure-results/

Each test generates JSON files containing execution details.


Step 4: Generate the Report

Once execution is complete, generate the HTML report.

allure serve allure-results

This command:

  • Builds the report
  • Starts a local server
  • Opens the report automatically in your browser

Understanding the Report Dashboard

The Allure dashboard provides valuable insights.

You'll find:

  • Total tests
  • Passed tests
  • Failed tests
  • Skipped tests
  • Duration
  • Success rate

This overview helps teams quickly assess test health.


Test Details

Clicking a test reveals:

  • Execution steps
  • Attachments
  • Error messages
  • Stack traces
  • Screenshots
  • Execution time

This detailed view speeds up root cause analysis.


Adding Screenshots to Allure

Capturing screenshots on failures greatly improves debugging.

Example helper method:

@Attachment(
value = "Failure Screenshot",
type = "image/png"
)

public byte[] attachScreenshot(Page page){

    return page.screenshot();

}

When called during a failed test, the screenshot is embedded directly in the report.


Attaching Text Logs

You can also include custom logs.

@Attachment(
value = "Execution Log",
type = "text/plain"
)

public String attachLog(String message){

    return message;

}

This is useful for recording API responses, SQL queries, or business events.


Attaching Videos

If Playwright records execution videos, attach them to the report.

@Attachment(
value = "Execution Video",
type = "video/webm"
)

public byte[] attachVideo(Path path)
throws IOException{

    return Files.readAllBytes(path);

}

Videos are invaluable when debugging intermittent failures.


Adding Test Descriptions

Allure supports annotations that make reports more readable.

@Description("Verify successful login using valid credentials")

@Test

public void loginTest(){

}

Descriptions appear directly in the report.


Categorizing Tests

Use features and stories to organize tests.

@Feature("Authentication")

@Story("Login")

@Test

public void loginTest(){

}

Reports become easier to navigate, especially in large projects.


Organizing the Project

Recommended folder structure:

project

├── allure-results

├── allure-report

├── screenshots

├── videos

├── src

├── pom.xml

└── testng.xml

Keeping artifacts separate improves project organization.


Best Practices

Capture Screenshots Only on Failure

Avoid capturing screenshots for every successful test to reduce storage usage.


Add Meaningful Descriptions

Use annotations to explain the purpose of each test.


Attach Logs for Failed Scenarios

Include:

  • API responses
  • Request payloads
  • Browser console logs
  • Custom debug messages

These details make troubleshooting much easier.


Store Environment Information

Create an environment.properties file containing details such as:

  • Browser
  • Operating system
  • Java version
  • Playwright version
  • Test environment

This information appears in the report and helps reproduce issues.


Archive Reports in CI/CD

When running tests in Jenkins, GitHub Actions, or Azure DevOps, archive the generated Allure reports as build artifacts. This allows team members to review historical execution results.


Common Interview Questions

Why is Allure preferred over the default TestNG report?

Because it provides interactive reports with screenshots, attachments, execution history, and a richer user experience.


How do you generate an Allure report?

Execute the tests and then run:

allure serve allure-results

Can Allure attach screenshots automatically?

Yes. By using the @Attachment annotation and capturing screenshots during test failures.


What types of files can be attached to Allure reports?

  • Screenshots
  • Videos
  • Text logs
  • JSON responses
  • XML files
  • PDFs
  • Any file that helps explain the test outcome

Conclusion

A good reporting solution is essential for every automation framework.

Allure Reports transform raw test execution data into an interactive, easy-to-understand dashboard that helps testers, developers, and managers quickly identify issues.

In this guide, you learned how to:

  • Integrate Allure with Playwright
  • Configure Maven
  • Generate reports
  • Attach screenshots, videos, and logs
  • Organize test results
  • Follow enterprise reporting best practices

By combining Playwright, TestNG, and Allure Reports, you can build a professional automation framework that is scalable, maintainable, and ready for enterprise projects.

Wednesday, 8 July 2026

Playwright TestNG Framework Setup Using Java (Step-by-Step Guide)

As automation projects grow, writing standalone Playwright scripts becomes difficult to maintain. Enterprise teams use testing frameworks like TestNG to organize test execution, manage test suites, perform assertions, generate reports, and execute tests in parallel.

In this guide, we'll build a Playwright TestNG framework from scratch using Java and follow industry best practices.


Why Use TestNG with Playwright?

TestNG offers several features that complement Playwright:

  • Test annotations (@Test, @BeforeMethod, @AfterMethod)
  • Test grouping
  • Parallel execution
  • Parameterized tests
  • Test dependencies
  • HTML reports
  • Easy integration with CI/CD tools

Combining Playwright with TestNG results in a scalable and maintainable automation framework.


Prerequisites

Before starting, ensure you have:

  • Java 17 or later
  • Maven installed
  • IntelliJ IDEA or Eclipse
  • Basic knowledge of Playwright
  • Browser binaries installed

Step 1: Create a Maven Project

Create a new Maven project in your IDE.

Suggested structure:

playwright-testng-framework
│
├── src
│   ├── main
│   │    └── java
│   │         ├── pages
│   │         ├── utils
│   │         └── base
│   │
│   └── test
│        └── java
│             └── tests
│
├── testng.xml
├── pom.xml
└── README.md

Step 2: Add Dependencies

Add the required dependencies to your pom.xml.

<dependencies>

    <dependency>
        <groupId>com.microsoft.playwright</groupId>
        <artifactId>playwright</artifactId>
        <version>1.55.0</version>
    </dependency>

    <dependency>
        <groupId>org.testng</groupId>
        <artifactId>testng</artifactId>
        <version>7.11.0</version>
        <scope>test</scope>
    </dependency>

</dependencies>

Refresh the Maven project after saving the file.


Step 3: Create the BaseTest Class

The BaseTest class is responsible for browser setup and cleanup.

public class BaseTest {

    protected Playwright playwright;
    protected Browser browser;
    protected BrowserContext context;
    protected Page page;

    @BeforeMethod
    public void setup() {

        playwright = Playwright.create();

        browser = playwright.chromium().launch(
                new BrowserType.LaunchOptions()
                        .setHeadless(false));

        context = browser.newContext();

        page = context.newPage();
    }

    @AfterMethod
    public void tearDown() {

        context.close();
        browser.close();
        playwright.close();
    }
}

This ensures every test starts with a fresh browser session.


Step 4: Create a Page Object

Example LoginPage.java.

public class LoginPage {

    private final Page page;

    public LoginPage(Page page) {
        this.page = page;
    }

    public void login(String username, String password) {

        page.fill("#username", username);

        page.fill("#password", password);

        page.click("#loginButton");
    }
}

Keeping page interactions separate improves maintainability.


Step 5: Create Your First Test

Create a TestNG test class.

public class LoginTest extends BaseTest {

    @Test

    public void verifySuccessfulLogin() {

        page.navigate("https://example.com/login");

        LoginPage loginPage =
                new LoginPage(page);

        loginPage.login(
                "admin",
                "password123");

        assertThat(page)
                .hasTitle("Dashboard");
    }
}

Notice how the test focuses only on business logic while setup is handled by BaseTest.


Step 6: Create testng.xml

TestNG uses an XML file to define test suites.

<!DOCTYPE suite SYSTEM
"https://testng.org/testng-1.0.dtd">

<suite name="Playwright Suite">

    <test name="Smoke Tests">

        <classes>

            <class name="tests.LoginTest"/>

        </classes>

    </test>

</suite>

You can execute all tests by running this suite file.


Step 7: Execute Tests

You can run tests in several ways:

  • Directly from IntelliJ IDEA
  • Using Eclipse
  • From Maven
  • Through Jenkins or another CI/CD pipeline

Example Maven command:

mvn test

Parallel Execution

TestNG supports parallel execution through testng.xml.

Example:

<suite name="Playwright Suite"
       parallel="methods"
       thread-count="4">

</suite>

This executes four test methods simultaneously, reducing execution time.


Adding Groups

TestNG groups help organize test cases.

Example:

@Test(groups = {"Smoke"})
public void loginTest() {

}

You can create groups such as:

  • Smoke
  • Regression
  • Sanity
  • API
  • UI

Generating Reports

After execution, TestNG automatically creates reports inside the test-output folder.

Common reports include:

  • HTML report
  • Failed test report
  • Execution summary

These reports provide quick insight into test results.


Recommended Framework Structure

As your project grows, consider organizing it like this:

src
├── main
│   ├── base
│   ├── pages
│   ├── utils
│   ├── constants
│   └── listeners
│
├── test
│   ├── tests
│   ├── testdata
│   └── resources
│
├── reports
├── screenshots
├── videos
└── testng.xml

This structure is easy to maintain and scales well for enterprise projects.


Best Practices

Keep Test Logic Separate

Business actions belong in page classes, not in test methods.


Reuse Browser Initialization

Initialize Playwright once in the base class instead of repeating code.


Use Assertions Wisely

Verify business outcomes rather than implementation details.

Examples:

  • User logged in successfully
  • Order created
  • Invoice downloaded

Capture Screenshots on Failure

Integrate screenshot capture inside the @AfterMethod hook for failed tests.


Store Test Data Separately

Avoid hardcoding values.

Instead, use:

  • JSON
  • Excel
  • CSV
  • Properties files

Common Interview Questions

Why combine Playwright with TestNG?

TestNG provides test lifecycle management, reporting, grouping, parameterization, and parallel execution, making Playwright projects easier to manage.


What is the purpose of @BeforeMethod?

It runs before every test method and is commonly used to initialize the browser and create a new page.


What is the role of BaseTest?

BaseTest centralizes browser setup and cleanup, reducing code duplication across test classes.


Can Playwright tests run in parallel with TestNG?

Yes. By configuring parallel and thread-count in testng.xml, multiple tests can run simultaneously.


Conclusion

Using Playwright with TestNG is an excellent choice for building robust and maintainable Java automation frameworks.

In this tutorial, you learned how to:

  • Create a Maven project
  • Add Playwright and TestNG dependencies
  • Build a reusable BaseTest
  • Implement the Page Object Model
  • Configure testng.xml
  • Execute tests
  • Enable parallel execution
  • Apply framework best practices

With this foundation, you're ready to build enterprise-grade automation frameworks that are scalable, reusable, and easy to integrate with CI/CD pipelines.

Tuesday, 7 July 2026

How to Perform API Testing in Playwright Using Java (Complete Guide with Examples)

Modern applications rely heavily on APIs to exchange data between frontend and backend systems. While Playwright is widely known for browser automation, it also provides powerful built-in support for API testing.

This means you can automate both UI tests and REST API tests using the same framework, reducing the need for separate tools in many automation projects.

In this tutorial, you'll learn how to perform API testing in Playwright using Java with practical examples.


Why Use Playwright for API Testing?

Playwright provides a built-in HTTP client that supports:

  • GET requests
  • POST requests
  • PUT requests
  • PATCH requests
  • DELETE requests
  • Authentication
  • Custom headers
  • Query parameters
  • Response validation

Using one framework for both UI and API testing simplifies project maintenance and reduces dependencies.


Creating an API Request Context

Playwright uses an APIRequestContext to send HTTP requests.

Example:

Playwright playwright = Playwright.create();

APIRequestContext request =
    playwright.request().newContext();

The request context acts as the client for all API operations.


Sending a GET Request

Let's retrieve a list of users.

APIResponse response =
    request.get("https://reqres.in/api/users?page=2");

You can verify the response status:

System.out.println(response.status());

Expected output:

200

Reading the Response Body

To print the response content:

String body = response.text();

System.out.println(body);

This returns the JSON response as a string.


Sending a POST Request

POST requests are commonly used to create resources.

Example:

String json = """
{
  "name":"John",
  "job":"QA Engineer"
}
""";

APIResponse response =
    request.post(
        "https://reqres.in/api/users",
        RequestOptions.create()
            .setData(json)
    );

Expected status:

201 Created

Sending a PUT Request

PUT requests update existing resources.

String updateJson = """
{
  "name":"John",
  "job":"Senior QA Engineer"
}
""";

APIResponse response =
    request.put(
        "https://reqres.in/api/users/2",
        RequestOptions.create()
            .setData(updateJson)
    );

Sending a DELETE Request

Deleting resources is equally simple.

APIResponse response =
    request.delete(
        "https://reqres.in/api/users/2"
    );

Expected response:

204 No Content

Adding Custom Headers

Many APIs require custom headers.

Example:

APIResponse response =
    request.get(
        "https://example.com/api/users",
        RequestOptions.create()
            .setHeader(
                "Authorization",
                "Bearer YOUR_TOKEN"
            )
            .setHeader(
                "Content-Type",
                "application/json"
            )
    );

Sending Query Parameters

Suppose the API accepts pagination.

APIResponse response =
    request.get(
        "https://reqres.in/api/users?page=2"
    );

Alternatively, build URLs dynamically for different test scenarios.


Validating the Response

A good API test should verify more than the status code.

Example:

assert response.status() == 200;

assert response.text().contains("Michael");

You can validate:

  • Status code
  • Response body
  • Headers
  • Response time
  • Business data

Authentication

Many enterprise APIs use Bearer tokens.

Example:

APIRequestContext request =
    playwright.request().newContext(
        new APIRequest.NewContextOptions()
            .setExtraHTTPHeaders(
                Map.of(
                    "Authorization",
                    "Bearer YOUR_ACCESS_TOKEN"
                )
            )
    );

This automatically sends the token with every request.


Complete Example

Playwright playwright = Playwright.create();

APIRequestContext request =
        playwright.request().newContext();

APIResponse response =
        request.get(
            "https://reqres.in/api/users?page=2"
        );

System.out.println(
        response.status()
);

System.out.println(
        response.text()
);

request.dispose();
playwright.close();

Best Practices

Reuse the API Request Context

Create one request context and reuse it throughout your test suite instead of creating a new one for every request.


Keep Test Data Separate

Store request payloads in JSON files instead of hardcoding them inside test classes.

Example:

testdata/
    createUser.json
    updateUser.json

Validate Business Responses

Don't verify only the status code.

Also validate:

  • Response fields
  • Error messages
  • IDs
  • Dates
  • Business rules

Use Environment Variables

Store:

  • Base URL
  • Tokens
  • Credentials

inside configuration files rather than directly in code.


Common Interview Questions

Can Playwright perform API testing?

Yes.

Playwright includes a built-in HTTP client through APIRequestContext.


Which HTTP methods does Playwright support?

  • GET
  • POST
  • PUT
  • PATCH
  • DELETE

Why use Playwright for API testing?

Because it allows teams to automate both UI and API tests using the same framework, improving consistency and reducing maintenance.


How do you authenticate API requests?

Use request headers with an Authorization token or configure default headers when creating the request context.


Conclusion

Playwright is much more than a browser automation framework. Its built-in API testing capabilities make it a powerful choice for modern automation engineers.

In this tutorial, you learned how to:

  • Create an API request context
  • Send GET, POST, PUT, and DELETE requests
  • Validate responses
  • Add authentication
  • Use headers and query parameters
  • Follow best practices for scalable API automation

By combining UI and API testing within the same Playwright project, you can build efficient, maintainable, and enterprise-ready automation frameworks.

Monday, 6 July 2026

How to Handle Multiple Browser Tabs and Windows in Playwright Using Java (Complete Guide)

Modern web applications often open new browser tabs or windows during user interactions. These may appear when users:

  • Download reports
  • Open help documentation
  • Sign in using Google or Microsoft
  • Complete online payments
  • View invoices
  • Open product details in a new tab

As an automation engineer, you need to switch between these tabs and windows seamlessly.

In this tutorial, we'll learn how to handle multiple browser tabs and windows in Playwright using Java.


Understanding Browser Tabs in Playwright

Unlike older automation frameworks, Playwright treats every browser tab as a Page object.

Whenever a new tab or popup opens, Playwright creates a new Page instance.

This makes switching between tabs much simpler.


Example Scenario

Imagine an e-commerce application.

  1. Open the home page.
  2. Click View Product.
  3. Product opens in a new tab.
  4. Verify the product title.
  5. Close the product tab.
  6. Continue testing on the original tab.

Let's automate this workflow.


Waiting for a New Browser Tab

Playwright provides the waitForPopup() method.

Page productPage = page.waitForPopup(() -> {
    page.locator("#viewProduct").click();
});

What happens here?

  • Playwright waits for a new tab.
  • The click action triggers the popup.
  • The new tab is returned as a Page object.

Verify the New Page

Once the new tab opens, you can interact with it just like the original page.

System.out.println(productPage.title());

productPage.locator("#addToCart").click();

No additional switching commands are required.


Getting the URL of the New Tab

System.out.println(productPage.url());

You can also verify it using assertions.

assertThat(productPage)
        .hasURL("https://example.com/product");

Closing the Popup Window

Once you're done with the popup:

productPage.close();

After closing, your original page remains active.


Working with Multiple Tabs

Suppose your application opens several tabs.

Example:

Page reportsPage = page.waitForPopup(() -> {
    page.locator("#reports").click();
});

Page invoicePage = page.waitForPopup(() -> {
    page.locator("#invoice").click();
});

Now you have two separate Page objects.

You can interact with each independently.


Managing All Open Pages

Playwright stores all open pages inside the browser context.

BrowserContext context = browser.newContext();

List<Page> pages = context.pages();

System.out.println(pages.size());

This returns every open browser tab.


Switching Between Tabs

Switching is simple because each tab has its own Page reference.

Page homePage = pages.get(0);

Page reportsPage = pages.get(1);

Page invoicePage = pages.get(2);

Simply use the desired Page object.


Example: Google Login Popup

Many websites use OAuth authentication.

Example:

  1. Click Sign in with Google
  2. Google login opens in a new window.
  3. Enter credentials.
  4. Return to application.
Page googleLogin = page.waitForPopup(() -> {

    page.locator("#googleLogin").click();

});

googleLogin.locator("#identifierId")
           .fill("user@example.com");

This approach works for any external authentication window.


Handling Multiple Browser Windows

Playwright treats browser windows exactly like browser tabs.

No special API is required.

Whether the application opens:

  • A new tab
  • A new browser window

Playwright returns a Page object.


Best Practices

Always Use waitForPopup()

Avoid:

page.locator("#openReport").click();

Then trying to search for the new page afterwards.

Instead:

Page reportPage =
    page.waitForPopup(() -> {

        page.locator("#openReport").click();

    });

This prevents timing issues.


Store Page References

Use meaningful variable names.

Good:

loginPage

paymentPage

reportPage

invoicePage

Avoid:

page1

page2

page3

Close Unused Tabs

Leaving unnecessary tabs open can:

  • Consume memory
  • Slow down execution
  • Make debugging harder

Always close tabs when they are no longer needed.


Verify Navigation

After switching tabs, verify:

  • URL
  • Page title
  • Key elements

Example:

assertThat(productPage)
        .hasTitle("Laptop");

Common Mistakes

Mistake 1

Using the original page after opening a popup.

Incorrect:

page.locator("#checkout").click();

Correct:

checkoutPage.locator("#checkout").click();

Mistake 2

Using hard waits.

Avoid:

Thread.sleep(5000);

Playwright automatically waits for the popup.


Complete Example

BrowserContext context =
        browser.newContext();

Page page = context.newPage();

page.navigate("https://example.com");

Page popup =
        page.waitForPopup(() -> {

            page.locator("#openWindow").click();

        });

System.out.println(popup.title());

popup.locator("#continue")
     .click();

popup.close();

System.out.println(page.title());

Interview Questions

How does Playwright represent a browser tab?

Every browser tab or browser window is represented by a Page object.


Which method waits for a new browser tab?

waitForPopup()

How do you retrieve all open tabs?

context.pages()

Can Playwright automate multiple browser windows?

Yes.

Browser windows and tabs are handled in the same way using separate Page objects.


Conclusion

Handling multiple browser tabs and windows is an essential skill for automation engineers working on enterprise applications.

Playwright makes this process simple by representing each browser tab as a Page object and providing APIs like waitForPopup() and context.pages().

In this tutorial, you learned:

  • How to wait for new tabs
  • How to switch between browser windows
  • How to verify popup content
  • How to close windows
  • Best practices for multi-tab automation

With these techniques, you'll be able to automate payment gateways, report downloads, external authentication flows, and many other real-world scenarios confidently.

Saturday, 4 July 2026

How to Handle Frames and iFrames in Playwright Using Java

Modern web applications frequently use frames and iFrames to embed content from external or internal sources.

Common examples include:

  • Payment gateways
  • Advertisement sections
  • Chat widgets
  • Embedded videos
  • Document viewers
  • Third-party integrations

As automation engineers, we must know how to switch into these frames and interact with elements inside them.

In this article, we'll learn how to handle frames and iFrames in Playwright using Java.


What is an iFrame?

An iFrame (Inline Frame) is an HTML element that loads another webpage inside the current webpage.

Example:

<iframe id="paymentFrame" src="payment.html"></iframe>

Elements inside an iFrame are isolated from the parent page.

This means the following code will fail:

page.locator("#cardNumber").fill("1234567890123456");

Playwright cannot access elements inside an iFrame directly from the main page context.


How Playwright Handles Frames

Playwright provides the frameLocator() method to interact with frames easily.

Example:

FrameLocator paymentFrame =
        page.frameLocator("#paymentFrame");

paymentFrame
        .locator("#cardNumber")
        .fill("1234567890123456");

This automatically switches into the frame before performing the action.


Handling Frames Using Frame Locator

Consider the following HTML:

<iframe id="loginFrame"></iframe>

Automation example:

page.frameLocator("#loginFrame")
        .locator("#username")
        .fill("admin");

page.frameLocator("#loginFrame")
        .locator("#password")
        .fill("password");

page.frameLocator("#loginFrame")
        .locator("#loginButton")
        .click();

Storing Frame Reference

If multiple interactions occur inside the same frame, storing the frame reference improves readability.

Example:

FrameLocator loginFrame =
        page.frameLocator("#loginFrame");

loginFrame.locator("#username")
        .fill("admin");

loginFrame.locator("#password")
        .fill("password");

loginFrame.locator("#loginButton")
        .click();

Accessing Frames by Name

Some applications provide frame names instead of IDs.

Example:

Frame frame =
        page.frame("paymentFrame");

You can then interact with elements:

frame.locator("#cardNumber")
        .fill("1234567890123456");

Accessing Frames by URL

Sometimes frames don't have IDs or names.

Example:

Frame frame =
        page.frameByUrl("**/payment/**");

This is useful when working with third-party integrations.


Handling Nested Frames

Some applications contain frames inside other frames.

Example:

page.frameLocator("#parentFrame")
        .frameLocator("#childFrame")
        .locator("#submitButton")
        .click();

Playwright makes nested frame handling very simple compared to traditional automation frameworks.


Getting All Available Frames

You can retrieve all frames present on the page.

Example:

for (Frame frame : page.frames()) {
    System.out.println(frame.url());
}

This is useful during debugging.


Real World Example: Payment Gateway

Consider an online shopping application.

Steps:

  1. Open checkout page.
  2. Payment gateway opens inside an iFrame.
  3. Enter card details.
  4. Submit payment.

Example:

FrameLocator paymentFrame =
        page.frameLocator("#paymentFrame");

paymentFrame.locator("#cardNumber")
        .fill("4111111111111111");

paymentFrame.locator("#expiryDate")
        .fill("12/30");

paymentFrame.locator("#cvv")
        .fill("123");

paymentFrame.locator("#payNow")
        .click();

Common Mistakes

Using Page Locator Instead of Frame Locator

Incorrect:

page.locator("#cardNumber")
        .fill("4111111111111111");

Correct:

page.frameLocator("#paymentFrame")
        .locator("#cardNumber")
        .fill("4111111111111111");

Using Hardcoded Waits

Avoid:

Thread.sleep(5000);

Playwright automatically waits for frames and elements when using locators.


Best Practices

Prefer frameLocator()

frameLocator() is cleaner and easier to maintain than manual frame handling.


Store Frame References

When interacting multiple times with the same frame, store the reference in a variable.


Use Meaningful Variable Names

Good examples:

paymentFrame
loginFrame
chatWidgetFrame

Avoid:

frame1
frame2

Validate Frame Visibility

Before interacting with frames, verify they are loaded properly.

Example:

assertThat(
        page.locator("#paymentFrame")
).isVisible();

Interview Questions

What is the difference between a Frame and an iFrame?

An iFrame is an HTML element that embeds another document inside the current page.

In automation testing, both are generally handled similarly.


Which Playwright method is commonly used to handle iFrames?

frameLocator()

How do you handle nested frames?

Use chained frame locators:

frameLocator()
        .frameLocator()

How can you retrieve all frames available on a page?

page.frames();

Conclusion

Frames and iFrames are widely used in modern web applications, especially in enterprise systems and third-party integrations.

Fortunately, Playwright provides simple and powerful APIs to handle them efficiently.

In this article, you learned:

  • What frames and iFrames are
  • How to use frameLocator()
  • How to handle nested frames
  • How to retrieve frame information
  • Best practices for frame automation

Mastering frame handling will make your Playwright framework more robust and capable of handling real-world applications.

In the next article, we'll learn how to handle multiple browser tabs and windows in Playwright using Java.