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 tutorial, I will explain how to IntegrateAllure Report 3 with Playwright, Java Cucumber, and JUnit5.
Before starting, make sure to install Allure on your machine. Refer to this tutorial to install allure – What is Allure Report?.
Why Combine Playwright, Cucumber, JUnit5 and Allure?
Playwright provides modern, robust automation for testing web apps across Chromium, Firefox, and WebKit.
Cucumber enables BDD, allowing tests to be written in a natural language format using Gherkin syntax.
Allure Report generates easy-to-understand and aesthetically pleasing test reports, enhancing the feedback loop.
JUnit 5 provides powerful features like parameterized tests, extensions, and a modular architecture for organized and manageable test suites, parallel execution
Together, they empower teams to write readable tests, run them across multiple browsers, and analyze results visually.
Create a folder – features within src/test/resources to create test scenarios in the Feature file.
Feature file should be saved as an extension of .feature. Add the test scenarios in this feature file. I have added sample test scenarios. In this feature file. The test scenarios are written in Gherkinslanguage.
@allure.label.parent_suite:WebInterface
@allure.label.sub_suite:Login Page
@allure.label.owner:Vibha
Feature: Login to HRM Application
Background:
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
@ValidCredentials @Critical
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 @High
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 @Medium
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
7. Create the step definition class in src/test/java
8. Create a JUnit Cucumber Runner class
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.
Add Allure Report plugin in the Test Runner to generate the Allure Report.
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
9. Specify Allure Plugin in junit-platform.properties
Specify the allure plugin in junit-platform.properties which should be placed in src/test/resources:
Allure, by default, saves test results in the project’s root directory. However, it is recommended to store your test results in the build output directory.
To configure this, create an allure.properties file and place it in the test resources directory of your project, which is typically located at src/test/resources:
In the image below, we can see that one test failed and four passed out of five tests.
This will create the allure-results folder with all the test reports within build folder. These files will be used to generate Allure Report.
12. How to Generate a Report
allure generate processes the test results and saves an HTML report into the allure-report directory. To view the report, use the allure open command.
allure serve creates the same report as allure generate, then automatically opens the main page of the report in a web browser.
Use the command below to generate the Allure Report
allure serve build/allure-results
This will generate the beautiful Allure Test Report as shown below.
13. How to View a Report
Test reports generated with Allure Report are basically small HTML websites intended to be viewed in a web browser.
Title
A human-readable title of the test. If not provided, the function name is used instead.
Tags
Any number of short terms the test is related to. Usually it’s a good idea to list relevant features that are being tested. Tags can then be used for filtering.
Owner
The team member who is responsible for the test’s stability. For example, this can be the test’s author, the leading developer of the feature being tested, etc.
Severity
A value indicating how important the test is. This may give the future reader an idea of how to prioritize the investigations of different test failures.
Allowed values are: “trivial”, “minor”, “normal”, “critical”, and “blocker”.
Support for Scenario Outlines
Allure Cucumber-JVM provides complete support for Scenario Outlines, a feature of Cucumber-JVM that allows for parametrized tests. No special setup is needed to take advantage of this capability within Allure.
Attachment
In Allure reports, you have the ability to attach various types of files, which can greatly enhance the comprehensibility of the report. A common practice is to attach screenshots that capture the state of the user interface at specific moments during test execution.
Allure Cucumber-JVM offers multiple methods for creating attachments, whether from pre-existing files or from content generated on-the-fly.
Graphs in Allure Report
Graphs allow you to see different statistics collected from the test data: status breakdown or severity and duration diagrams.
Timeline in Allure Report
The timeline tab visualizes retrospective test execution, allure adaptors collect precise timings of tests, and here on this tab, they are arranged accordingly to their sequential or parallel timing structure.
Generate Reports with the Allure Awesome Plugin
Allure 3 Report also features an advanced report generator plugin – Allure Awesome. It supports additional configuration options, such as generating the report as a single HTML file, setting the theme, custom branding and language of the generated report, and taking known issues into account.
To manually generate a customized Allure Awesome report, use the awesome command:
allure awesome build/allure-results
This will create the allure-report folder with all the test files and index.html report.
Open “index.html“, as this is an HTML report, and open it with the browser. The below image shows index.html.
Right click on index.html->Open In ->Browser ->Edge( any browser).
Summary:
1.Add required dependencies – Include Playwright, JUnit 5, and Allure JUnit5 in pom.xml, and configure the Allure Maven plugin.
2. Write Cucumber Scenarios in Feature File – Define the feature files using Gherkin syntax (.feature) and place them in the appropriate directory (src/test/resources/features).
3. Implement Step Definitions – Create step definitions in Java to connect the Gherkin scenarios to Playwright actions. Ensure these are correctly annotated with Cucumber’s annotations.
4. Create Test Runner – Setup a TestNG runner that combines Cucumber options and ensures Allure configurations are included.
5. Configure Allure Reporting – Utilize Allure annotations like @AllureFeature and @AllureStory within your tests to categorize and logically group test cases. Create an allure.properties file in src/test/resources with necessary configurations like resultsDirectory and linkPattern.
6. Execute Tests and Generate Reports – Execute mvn clean test, then run allure serve allure-results to build and open the HTML report.
Congratulations!! We have integrated an allure report with Playwright, Java Cucumber, and TestNG. I hope this tutorial is useful to you.
Create a folder – features within src/test/resources to create test scenarios in the Feature file.
Feature file should be saved as an extension of .feature. Add the test scenarios in this feature file. I have added sample test scenarios. In this feature file. The test scenarios are written in Gherkinslanguage.
@allure.label.parent_suite:WebInterface
@allure.label.sub_suite:Login Page
@allure.label.owner:Vibha
Feature: Login to HRM Application
Background:
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
@ValidCredentials @Critical
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 @High
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 @Medium
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
7. Create the step definition class in src/test/java
8. Create a TestNG Cucumber Runner class
We need to create a class called Runner class to run the tests. This class will use the TestNG annotation @Test, which tells TestNG what is the test runner class.
Add Allure Report plugin in the Test Runner to generate the Allure Report.
Note:- @Test annotation marks this class as part of the test. So, if we will remove this annotation, the Allure Report executesCucumberRunnerTests as a separate test suite, so there will be duplicate results.
9. Create testng.xml for the project
<?xml version = "1.0"encoding = "UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name = "Suite1">
<test name = "Test Demo">
<classes>
<class name = "com.example.runner.CucumberRunnerTests"/>
</classes>
</test>
</suite>
10. Specifying Allure Results location
Allure, by default, saves test results in the project’s root directory. Still, it is recommended to store your test results in the build output directory.
To configure this, create an allure.properties file and place it in the test resources directory of your project, which is typically located at src/test/resources:
In the image below, we can see that one test failed and four passed out of five tests.
This will create the allure-results folder with all the test reports within build folder. These files will be used to generate Allure Report.
12. How to Generate a Report
allure generate processes the test results and saves an HTML report into the allure-report directory. To view the report, use the allure open command.
allure serve creates the same report as allure generate, then automatically opens the main page of the report in a web browser.
Use the command below to generate the Allure Report
allure serve build/allure-results
This will generate the beautiful Allure Test Report as shown below.
13. How to View a Report
Test reports generated with Allure Report are basically small HTML websites intended to be viewed in a web browser.
Title
A human-readable title of the test. If not provided, the function name is used instead.
Tags
Any number of short terms the test is related to. Usually it’s a good idea to list relevant features that are being tested. Tags can then be used for filtering.
Owner
The team member who is responsible for the test’s stability. For example, this can be the test’s author, the leading developer of the feature being tested, etc.
Severity
A value indicating how important the test is. This give the future reader an idea of how to prioritize the investigations of different test failures.
Allowed values are: “trivial”, “minor”, “normal”, “critical”, and “blocker”.
Support for Scenario Outlines
Allure Cucumber-JVM provides complete support for Scenario Outlines, a feature of Cucumber-JVM that allows for parametrized tests. No special setup is needed to take advantage of this capability within Allure.
Attachment
In Allure reports, you have the ability to attach various types of files, which can greatly enhance the comprehensibility of the report. A common practice is to attach screenshots that capture the state of the user interface at specific moments during test execution.
Allure Cucumber-JVM offers multiple methods for creating attachments, whether from pre-existing files or from content generated on-the-fly.
Graphs in Allure Report
Graphs allow you to see different statistics collected from the test data: status breakdown or severity and duration diagrams.
Timeline in Allure Report
The timeline tab visualizes retrospective test execution, allure adaptors collect precise timings of tests, and here on this tab, they are arranged accordingly to their sequential or parallel timing structure.
Generate Reports with the Allure Awesome Plugin
Allure 3 Report also features an advanced report generator plugin – Allure Awesome. It supports additional configuration options, such as generating the report as a single HTML file, setting the theme, custom branding and language of the generated report, and taking known issues into account.
To manually generate a customized Allure Awesome report, use the awesome command:
allure awesome build/allure-results
This will create the allure-report folder with all the test files and index.html report.
Open “index.html“, as this is an HTML report, and open it with the browser. The below image shows index.html.
Right click on index.html->Open In ->Browser ->Edge( any browser).
Summary:
1.Add required dependencies – Include Playwright, TestNG, and Allure TestNG in pom.xml, and configure the Allure Maven plugin.
2. Write Cucumber Scenarios in Feature File – Define the feature files using Gherkin syntax (.feature) and place them in the appropriate directory (src/test/resources/features).
3. Implement Step Definitions – Create step definitions in Java to connect the Gherkin scenarios to Playwright actions. Ensure these are correctly annotated with Cucumber’s annotations.
4. Create Test Runner – Setup a TestNG runner that combines Cucumber options and ensures Allure configurations are included.
5. Configure Allure Reporting – Utilize Allure annotations like @AllureFeature and @AllureStory within your tests to categorize and logically group test cases. Create an allure.properties file in src/test/resources with necessary configurations like resultsDirectory and linkPattern.
6. Execute Tests and Generate Reports – Execute mvn clean test, then run allure serve allure-results to build and open the HTML report.
Congratulations!! We have integrated an allure report with Playwright, Java Cucumber, and TestNG. I hope this tutorial is useful to you.
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.
In this tutorial, I will explain how to IntegrateAllure Report 3 with Playwright, Javaand JUnit5.
What is an Allure Report?
Allure Framework is a flexible, lightweight, multi-language test report tool. It shows a very concise representation of what has been tested in a neat web report form. It also allows everyone participating in the development process to extract maximum useful information from the everyday execution of tests.
package com.example.pages;
import com.microsoft.playwright.Locator;
import com.microsoft.playwright.Page;
import io.qameta.allure.Step;
public class LoginPage {
private final Page page;
// Locators
private final Locator usernameLocator;
private final Locator passwordLocator;
private final Locator submitLocator;
private final Locator invalidCredentialsLocator;
private final Locator missingUsernameErrorMessageLocator;
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.missingUsernameErrorMessageLocator = page.locator("//span[contains(@class, 'oxd-text oxd-text--span oxd-input-field-error-message oxd-input-group__message')]");
}
@Step("Enter credentials")
public void login(String user, String pass){
usernameLocator.fill(user);
passwordLocator.fill(pass);
submitLocator.click();
}
@Step("Get Error Message for invalid credentials")
public String getErrorMessage () {
return invalidCredentialsLocator.textContent();
}
@Step("Get Error Message for missing username")
public String getMissingUsernameErrorMessage () {
return missingUsernameErrorMessageLocator.textContent();
}
}
DashboardPage
package com.example.pages;
import com.microsoft.playwright.Locator;
import com.microsoft.playwright.Page;
import io.qameta.allure.Step;
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\")]");
this.assignLeaveLocator = page.locator("//[contains(@text, \"Assign Leave\")]");
this.leaveListLocator = page.getByTitle("Leave List");
}
@Step("Get Heading of Dashboard page")
public String getDashboardPageHeading() {
return headingLocator.textContent();
}
}
4. Create the utility package in src/test/java
BaseClass
This class contains the common variables and methods used throughout the project, like setup and teardown methods.
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();
}
}
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.
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” with different set of invalid test data.
Verify that the user is able to login to the application successfully.
Creating a Login Page Test Class:
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;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvFileSource;
import io.qameta.allure.Description;
import io.qameta.allure.Issue;
import io.qameta.allure.Link;
import io.qameta.allure.Owner;
import io.qameta.allure.Severity;
import io.qameta.allure.TmsLink;
import org.junit.jupiter.api.DisplayName;
import static io.qameta.allure.SeverityLevel.CRITICAL;
public class LoginTests extends BaseTests {
@ParameterizedTest
@CsvFileSource(files = "src/test/resources/testData/credentials.csv", numLinesToSkip = 1)
@DisplayName("Login Authentication")
@Description("This test attempts to log into the website using a incorrect username and a password. Fails if any error happens.")
@Severity(CRITICAL)
@Owner("Vibha Singh")
@Link(name = "Website", url = "https://opensource-demo.orangehrmlive.com/web/index.php/auth/login")
@Issue("AUTH-123")
@TmsLink("TMS-456")
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, "Incorrect error message is displayed");
}
@Test
@DisplayName("Successful Login Authentication")
@Description("This test attempts to log into the website using a correct username and a password. Fails if any error happens.")
@Severity(CRITICAL)
@Owner("Vibha Singh")
@Link(name = "Website", url = "https://opensource-demo.orangehrmlive.com/web/index.php/auth/login")
@Issue("AUTH-124")
@TmsLink("TMS-460")
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");
}
}
Use the @Description() annotation to set a description statically or use the description() method to set it dynamically in runtime.
@Description("This test attempts to log into the website using a invalid login and a password that result in error")
6. Specifying Allure Results location
Allure, by default, saves test results in the project’s root directory. However, it is recommended to store your test results in the build output directory.
To configure this, create an junit-platform.properties file and place it in the test resources directory of your project, which is typically located at src/test/resources:
This will create the allure-results folder with all the test reports within build folder. These files will be used to generate Allure Report.
8. How to Generate an Allure Report
allure generate processes the test results and saves an HTML report into the allure-report directory. To view the report, use the allure open command.
allure serve creates the same report as allure generate, then automatically opens the main page of the report in a web browser.
Use the command below to generate the Allure Report
allure serve build/allure-results
This will generate the beautiful Allure Test Report as shown below.
9. How to View a Report
Test reports generated with Allure Report are basically small HTML websites intended to be viewed in a web browser.
Title
A human-readable title of the test. If not provided, the function name is used instead.
Owner
The team member who is responsible for the test’s stability. For example, this can be the test’s author, the leading developer of the feature being tested, etc.
Severity
A value indicating how important the test is. This may give the future reader an idea of how to prioritize the investigations of different test failures.
Allowed values are: “trivial”, “minor”, “normal”, “critical”, and “blocker”.
Steps
Allure JUnit 5 provides three ways of creating steps and sub-steps: “annotated steps”, “lambda steps” and “no-op steps”
Links
We can provide list of links to webpages that may be useful for a reader investigating a test failure.
Graphs in Allure Report
Graphs allow you to see different statistics collected from the test data: status breakdown or severity and duration diagrams.
Timeline in Allure Report
The timeline tab visualizes retrospective test execution. Allure adaptors collect precise timings of tests. Here on this tab, they are arranged according to their sequential or parallel timing structure.
Generate Reports with the Allure Awesome Plugin
Allure 3 Report also features an advanced report generator plugin – Allure Awesome. It supports additional configuration options, such as generating the report as a single HTML file, setting the theme, custom branding and language of the generated report, and taking known issues into account.
To manually generate a customized Allure Awesome report, use the awesome command:
allure awesome build/allure-results
This will create the allure-report folder with all the test files and index.html report.
Open “index.html“, as this is an HTML report, and open it with the browser. The below image shows index.html.
Right click on index.html->Open In ->Browser ->Edge( any browser).
Summary:
Add required dependencies – Include Playwright, JUnit 5, and Allure JUnit5 in pom.xml, and configure the Allure Maven plugin.
Write Test Case – Implement test cases using JUnit 5 syntax, leveraging Playwright for browser-based actions. Use annotations like @Test, @BeforeEach and @AfterEach to structure your tests.
Enable JUnit 5 extension auto-detection – Create src/test/resources/junit-platform.properties and turn on extension autodetection.
Annotate tests & capture artifacts – Use Allure annotations (@Epic, @Feature, @Story, @Severity, @Description), log steps with Allure.step, and attach screenshots or other files.
Execute Tests and Generate Reports – Execute mvn clean test, then run allure serve allure-results to build and open the HTML report.
Congratulations!! We have integrated an allure report with Playwright, Java, and JUnit5. I hope this tutorial is useful to you.
In this tutorial, I will explain how to IntegrateAllure Report 3 with Playwright, Javaand TestNG.
What is an Allure Report?
Allure Framework is a flexible, lightweight, multi-language test report tool. It shows a very concise representation of what has been tested in a neat web report form. It also allows everyone participating in the development process to extract maximum useful information from the everyday execution of tests.
AllureAttachment class is created to attach binary data (the screenshot) to the currently running test in the Allure lifecycle. This gives a central attachment library for the framework.
package com.example.utils;
import io.qameta.allure.Attachment;
public abstract class AllureAttachments {
@Attachment(value = "Failure Screenshot", type = "image/png")
public static byte[] attachScreenshot(byte[] screenshot) {
return screenshot;
}
}
BaseClass
This class contains the common variables and methods used throughout the project, like setup and teardown methods.
package com.example.utils;
import com.microsoft.playwright.*;
import org.testng.annotations.*;
public class BaseClass {
// Shared between all tests in this class.
Playwright playwright;
Browser browser = null;
// New instance for each test method.
BrowserContext context;
public Page page;
public Page getPage() {
return 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();
}
}
TestListener
This class is a TestNG failure hook. Its job is to automatically capture a Playwright screenshot when a test fails. The screenshot is then attached to the Allure report.
By implementing ITestListener, we are plugging into the TestNG test lifecycle. That means this class gets called automatically whenever a test fails.
package com.example.utils;
import com.microsoft.playwright.Page;
import org.testng.ITestListener;
import org.testng.ITestResult;
public class TestListener implements ITestListener {
@Override
public void onTestFailure(ITestResult result) {
System.out.println("❌ onTestFailure triggered");
Page page = BaseClass.getPage();
if (page != null) {
byte[] screenshot = page.screenshot();
AllureAttachments.attachScreenshot(screenshot);
}
}
}
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.
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”
Verify that the user is able to login to the application successfully.
Verify the missing username generates error message “Required1”. This test will fail
Creating a Login Page Test Class:
package com.example.tests;
import com.example.pages.DashboardPage;
import com.example.pages.LoginPage;
import com.example.utils.BaseClass;
import com.example.utils.TestListener;
import io.qameta.allure.Description;
import io.qameta.allure.Owner;
import io.qameta.allure.Severity;
import org.testng.Assert;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static io.qameta.allure.SeverityLevel.*;
public class LoginTests extends BaseClass {
@Test
@Description("This test attempts to log into the website using a invalid login and a password that result in error")
@Severity(NORMAL)
@Owner("Vibha Singh")
public void unsuccessfulLogin() {
LoginPage loginPage = new LoginPage(page);
loginPage.login("abc","abc");
String actualErrorMessage = loginPage.getErrorMessage();
Assert.assertEquals(actualErrorMessage, "Invalid credentials");
}
@Test
@Description("This test attempts to log into the website using a valid login and a password")
@Severity(CRITICAL)
@Owner("Vibha Singh")
public void successfulLogin() {
LoginPage loginPage = new LoginPage(page);
loginPage.login("Admin","admin123");
DashboardPage dashboardPage = new DashboardPage(page);
String actualHeading = dashboardPage.getDashboardPageHeading();
Assert.assertEquals(actualHeading, "Dashboard");
}
@Test
@Description("This test attempts to log into the website using a blank login and a password that result in error")
@Severity(MINOR)
@Owner("Vibha Singh")
public void missingUsername() {
LoginPage loginPage = new LoginPage(page);
loginPage.login("","admin123");
String actualErrorMessage = loginPage.getMissingUsernameErrorMessage();
Assert.assertEquals(actualErrorMessage, "Required1");
}
}
Use the @Description() annotation to set a description statically or use the description() method to set it dynamically in runtime.
@Description("This test attempts to log into the website using a invalid login and a password that result in error")
6. Create testng.xml for the project
Mention the listener in the testng.xml. This is used for all the test classes.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Playwright test suite">
<listeners>
<listener class-name="com.example.utils.TestListener"/>
</listeners>
<test name="Integration of Playwright Java with TestNG">
<classes>
<class name="com.example.tests.LoginTests">
</class>
</classes>
</test>
</suite>
7. Specifying Allure Results location
Allure, by default, saves test results in the project’s root directory. However, it is recommended to store your test results in the build output directory.
To configure this, create an allure.properties file and place it in the test resources directory of your project, which is typically located at src/test/resources:
In the image below, we can see that one test failed and four passed out of three tests.
This will create the allure-results folder with all the test reports within build folder. These files will be used to generate Allure Report.
9. How to Generate an Allure Report
allure generate processes the test results and saves an HTML report into the allure-report directory. To view the report, use the allure open command.
allure serve creates the same report as allure generate, then automatically opens the main page of the report in a web browser.
Use the command below to generate the Allure Report
allure serve build/allure-results
This will generate the beautiful Allure Test Report as shown below.
10. How to View a Report
Test reports generated with Allure Report are basically small HTML websites intended to be viewed in a web browser.
Title
A human-readable title of the test. If not provided, the function name is used instead.
Owner
The team member who is responsible for the test’s stability. For example, this can be the test’s author, the leading developer of the feature being tested, etc.
Severity
A value indicating how important the test is. This may give the future reader an idea of how to prioritize the investigations of different test failures.
Allowed values are: “trivial”, “minor”, “normal”, “critical”, and “blocker”.
Steps
Allure TestNG provides three ways of creating steps and sub-steps: “annotated steps”, “lambda steps” and “no-op steps”. Define a test step with the given name.
Attachment
We can attach any sorts of files to your Allure report. Allure TestNG provides various ways to create an attachment, both from existing files or generated dynamically. To create an attachment using the Annotations API, define a method that returns some data and annotate it with @Attachment. Call the method at any point during your test.
Click on the highlighted portion and we can see the screenshot of the failed test.
Graphs in Allure Report
Graphs allow you to see different statistics collected from the test data: status breakdown or severity and duration diagrams.
Timeline in Allure Report
The timeline tab visualizes retrospective test execution. Allure adaptors collect precise timings of tests. Here on this tab, they are arranged according to their sequential or parallel timing structure.
Generate Reports with the Allure Awesome Plugin
Allure 3 Report also features an advanced report generator plugin – Allure Awesome. It supports additional configuration options, such as generating the report as a single HTML file, setting the theme, custom branding and language of the generated report, and taking known issues into account.
To manually generate a customized Allure Awesome report, use the awesome command:
allure awesome build/allure-results
Congratulations!! We have integrated an allure report with Cucumber, Selenium, and TestNG. I hope this tutorial is useful to you.
Welcome to the SQL Quiz! This blog post features 25 multiple-choice questions that explore Subqueries concept of SQL.
1. What is a subquery in SQL?
a) A query that modifies data b) A query within another SQL query c) A query that creates tables d) A query that deletes data
Answer 1
b) A query within another SQL query
A subquery is a query nested within another SQL query, often used to provide results for the outer query to act upon
2. Which of the following is true about sub-queries?
a) They execute after the main query executes b) They execute in parallel to the main query c) The user can execute the main query and then, if wanted, execute the sub-query d) They execute before the main query executes.
Answer 2
d) They execute before the main query executes.
The sub-query always executes before the execution of the main query. Subqueries are completed first. The result of the subquery is used as input for the outer query.
3. Which of the following is a method for writing a sub-query in a main query?
a) By using JOINS b) By using WHERE clause c) By using the GROUP BY clause d) By writing a SELECT statement embedded in the clause of another SELECT statement
Answer 3
d) By writing a SELECT statement embedded in the clause of another SELECT statement
A subquery is a complete query nested in the SELECT, FROM, HAVING, or WHERE clause of another query. The subquery must be enclosed in parentheses and have a SELECT and aFROM clause, at a minimum
4. What will the following subquery do?
SELECT employee_id FROM employees WHERE salary > (SELECT AVG(salary) FROM employees);
a) Select employees with below-average salary b) Select employees with above-average salary c) Throw an error d) Select all employees
Answer 4
b) Select employees with above-average salary
It selects employees whose salary is greater than the average salary of all employees.
5. In the given scenarios, which one would appropriately justify the usage of sub-query?
a) When we need to sum up values b) When we need to convert character values into date or number values c) When we need to select rows from a table with a condition that depends on the data from the same or different table. d) None of the above
Answer 5
c) When we need to select rows from a table with a condition that depends on the data from the same or different table.
6. Which of the following single-row operators can be used for writing a sub-query?
a) >= b) < c) = d) All of the above
Answer 6
d) All of the above
Single-row operators include =, >, <, >=, <=, and <>
7. In which scenario is a subquery most appropriately used?
a) To sort data in descending order b) To filter results based on aggregated data from another table c) To duplicate data between tables d) To drop tables from a database
Answer 7
b) To filter results based on aggregated data from another table
Subqueries are commonly used to filter or manipulate data based on the results of a secondary query, especially with aggregated data.
8. What would be the effect of this query?
SELECT * FROM employees WHERE department_id = (SELECT department_id FROM departments WHERE name = 'Sales');
a) List all departments b) List all employees in all departments c) List all employees in the Sales department d) List all employees not in Sales department
Answer 8
c) List all employees in the Sales department
The subquery finds the department_id for ‘Sales’, and the outer query selects employees matching that department.
9. Which of the following multi-row operators can be used with a sub-query?
a) IN b) ANY c) ALL d) All of the above
Answer 9
d) All of the above
Multiple-row subqueries return more than one row of results.Operators that can beused with multiple-row subqueries include IN, ALL, ANY, and EXISTS.
10. Can a subquery return multiple columns and be used in a WHERE clause?
a) Yes, if using EXISTS b) Yes, if using JOIN c) No, must return a single column d) Yes, always
Answer 10
c) No, must return a single column
In a WHERE clause, subqueries must return a single column to be a valid comparison or condition.
11. Which of the following is a correlated subquery?
Choose one option
a) SELECT * FROM employees WHERE salary > (SELECT AVG(salary) FROM employees);
b) SELECT * FROM employees e WHERE salary > (SELECT AVG(salary) FROM employees WHERE department_id = e.department_id);
c) SELECT * FROM employees WHERE salary IN (SELECT salary FROM employees);
d) SELECT * FROM employees WHERE id IN (SELECT id FROM departments);
Answer 11
b)
It refers to the outer query’s row, making it a correlated subquery that executes for each row processed by the outer query.
12. How many levels of nested subqueries does SQL typically allow?
Choose one option
a) One b) Two c) Unlimited d) 255
Answer 12
d) 255
SQL standards typically allow up to 255 levels of nested subqueries, though this might vary by database implementation.
13. Where can subqueries NOT be used in SQL?
Choose one option
a) FROM clause b) SELECT clause c) JOIN clause d) IN clause
Answer 13
c) JOIN clause
Subqueries are not included as part of JOIN conditions; they appear as tables need explicit relations in a join.
14. What will be the output of the below query?
SELECT * FROM employees
WHERE salary BETWEEN (SELECT max(salary) FROM employees WHERE department_id = 100) AND
(SELECT min(salary) FROM employees WHERE department_id = 100);
This query returns an error. What is the reason for the error?
a) A GROUP BY clause should be used as the function MAX is used b) Both the sub-queries cannot use the same department ID in the same outer query c) BETWEEN operator cannot be used with a sub-query d) SELECT clause should have columns mentioned and not a asterix ∗
Answer 14
c) BETWEEN operator cannot be used with a sub-query
15. What kind of subquery is this?
SELECT department_id FROM departments WHERE department_id = (SELECT MAX(department_id) FROM departments);
Choose one option
a) Scalar b) Correlated c) Non-correlated d) Nested
Answer 15
a) Scalar
This is a scalar subquery that returns a single value and is used to select the row matching the maximum department_id
16. Which of the following is a valid example of a subquery within an UPDATE statement?
a) UPDATE employees SET salary = salary * 1.1 WHERE department_id = (SELECT department_id FROM departments);
b) UPDATE employees SET salary = salary * 1.1 WHERE department_id = (SELECT department_id FROM departments WHERE name = 'HR');
c) UPDATE employees USING (SELECT department_id FROM departments WHERE name = 'HR');
d) UPDATE employees SET salary = 1.1 * (SELECT salary FROM employees);
Answer 16
b)
This query uses a subquery to update employee salaries pertaining to the HR department only.
17. What does the following SQL statement do?
DELETE FROM employees WHERE manager_id IN (SELECT employee_id FROM employees WHERE department_id = 3);
Choose one option
a) Deletes all employees b) Deletes managers in department 3 c) Deletes employees managed by those in department 3 d) Deletes all managers
Answer 17
c) Deletes employees managed by those in department 3
This usage of a subquery identifies all employees who report directly to managers specified in department 3.
18. What will be the outcome of the following query?
SELECT first_name, last_name, salary FROM employees WHERE salary ANY
(SELECT salary FROM employees);
a) It executes successfully giving the desired results b) It executes successfully but does not give the desired results c) It throws an ORA error d) It executes successfully and gives two values for each row obtained in the result set
Answer 18
c) It throws an ORA error
Multi-row operators cannot be used in single-row sub-queries and vice versa.
19. Which of the following is a valid use of a subquery in the FROM clause?
a) SELECT * FROM (SELECT column1 FROM table1) AS subquery;
b) SELECT * FROM table1 WHERE column1 = (SELECT column2 FROM table2);
c) SELECT * FROM table1 INNER JOIN (SELECT column2 FROM table2) AS subquery;
d) SELECT column1 FROM table1 WHERE column2 = (SELECT column2 FROM table2) AND column3 = (SELECT column3 FROM table3);
Answer 19
a)
A valid use of a subquery in the FROM clause is treating the subquery as a temporary table, allowing the outer query to select from it.
20. What will be the outcome of the following query?
SELECT first_name, last_name FROM employees WHERE emp_id NOT IN
(SELECT manager_id, hire_date FROM employees WHERE manager_id IS NOT NULL);
Choose one option
a) The NOT IN operator used is invalid b) The WHERE clause in the sub-query is incorrectly written c) The column in the sub-query SELECT clause should only be one when there’s an inequality used in the main query d) The sub-query uses the same table as the main query
Answer 20
c) The column in the sub-query SELECT clause should only be one when there’s an inequality used in the main query
The columns selected in the sub-query should be same as on the other side of comparison operator. Any inequality of data type or number of columns would result in an ORA error.
21. When used in a SELECT statement, a subquery is also known as a:
a) Derived table b) Join c) Index d) Constraint
Answer 21
a) Derived table
When a subquery is used in the FROM clause, it’s often called a derived or inline table.
22. Which SQL statement can include a subquery?
a) SELECT b) INSERT c) UPDATE d) All of the above
Answer 22
d) All of the above
Subqueries can be used in SELECT, INSERT, UPDATE, or DELETE statements.
23. Which keyword can be used to handle subqueries that may return NULL?
a) IGNORE b) IS NOT NULL c) NVL or COALESCE d) NULLIF
Answer 23
c) NVL or COALESCE
The NVL or COALESCE functions can replace NULLs with a specified value if returned by a subquery.
24. What is a correlated subquery?
a) A subquery that can be run independently b) A subquery that references columns from the outer query c) A subquery that updates multiple tables d) A subquery that creates indices
Answer 24
b) A subquery that references columns from the outer query
Correlated subqueries use values from the outer query and run once for each row processed by the outer query.
25. What is the result of a subquery that returns NULL when it’s used with the IN operator?
a) Always includes NULL b) Excludes all NULLs and matches c) Returns no rows d) Matches nothing, including NULL
Answer 25
d) Matches nothing, including NULL
A subquery with NULL makes IN operator skip or ignore such direct results, unless IS NULL is explicitly used.
We would love to hear from you! Please leave your comments and share your scores in the section below
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
Welcome to the SQL Quiz! This blog post features 25 multiple-choice questions that explore JOINS concept of SQL.
1. Which SQL JOIN returns only the rows that have matching values in both tables?
a) LEFT JOIN b) RIGHT JOIN c) INNER JOIN d) FULL OUTER JOIN
Answer 1
c) INNER JOIN
INNER JOIN matches records in both tables based on the join condition.
2. What does the following SQL query return?
SELECT products.product_name, orders.order_date
FROM products
INNER JOIN orders ON products.product_id = orders.product_id;
a) Only products that have been ordered b) All products, ordered and non-ordered c) All orders, regardless of the products d) None of the above
Answer 2
a) Only products that have been ordered
The INNER JOIN retrieves only products that have corresponding order records.
3. If you want all rows from the left table and only the matching rows from the right table, which join will you use?
a) INNER JOIN b) RIGHT JOIN c) LEFT JOIN d) FULL JOIN
Answer 3
c) LEFT JOIN
LEFT JOIN keeps all rows from the left table and adds NULLs for non-matching right table rows.
4. Which join type would you use to include all students and their respective grades, whether or not the grades are recorded?
SELECT students.student_name, grades.grade
FROM students
__________ JOIN grades ON students.student_id = grades.student_id;
a) INNER JOIN b) RIGHT JOIN c) LEFT JOIN d) FULL JOIN
Answer 4
c) LEFT JOIN
A LEFT JOIN includes all students and their grades, with NULL for students without recorded grades.
5. Which JOIN includes all records from both tables, filling NULLs where there are no matches?
a) INNER JOIN b) CROSS JOIN c) FULL OUTER JOIN d) SELF JOIN
Answer 5
c) FULL OUTER JOIN
FULL OUTER JOIN returns all rows from both tables with NULLs where no match exists.
6. What result does the following query produce?
SELECT a.name, b.address
FROM customers a
FULL OUTER JOIN orders b ON a.customer_id = b.customer_id;
a) Only matching customers and orders b) All customers and orders with matches and non-matches c) Customers without orders d) Orders without customers
Answer 6
b) All customers and orders with matches and non-matches
A FULL OUTER JOIN returns all records when there is a match in either table A or table B.
7. What does a CROSS JOIN do?
a) Matches only the common records b) Returns Cartesian product of two tables c) Returns only distinct records d) Works only on primary keys
Answer 7
b) Returns Cartesian product of two tables
CROSS JOIN multiplies all rows from one table with all rows of another.
8. What does the following SQL query do?
SELECT e.employee_id, e.name, d.department_name
FROM employees e
CROSS JOIN departments d;
a) Joins each employee with all departments b) Returns only matched employees and departments c) Lists employees without departments d) Lists departments without employees
Answer 8
a) Joins each employee with all departments
A CROSS JOIN creates a Cartesian product of both tables.
9. Which JOIN is used to join a table with itself?
a) CROSS JOIN b) SELF JOIN c) FULL OUTER JOIN d) NATURAL JOIN
Answer 9
b) SELF JOIN
SELF JOIN is useful for hierarchical or recursive relationships.
10. What is a primary use case for a self join in SQL?
a) To join two unrelated databases b) To compare rows within the same table c) To join tables with similar schemas d) To execute subqueries
Answer 10
b) To compare rows within the same table
SELECT a.employee_id, a.name, b.manager_id
FROM employees a
JOIN employees b ON a.manager_id = b.employee_id;
In the provided query, the employees table is joined with itself to find employees and their respective managers by matching employee IDs with manager IDs.
11. In a self join scenario, how would you distinguish between the different roles of the table being joined to itself?
Choose one option
a) Use different database instances b) Use table aliases c) Create a temporary table d) Use a different SQL dialect
Answer 11
b) Use table aliases
SELECT e1.name AS Employee, e2.name AS Manager
FROM employees e1
JOIN employees e2 ON e1.manager_id = e2.employee_id;
In a self join, table aliases are employed to differentiate between the roles of the same table. Here, `e1` represents employees, and `e2` represents managers, allowing us to fetch the employee names along with their corresponding managers.
12. To avoid a Cartesian product, always include a valid join condition in a WHERE clause.
Choose one option
a) True b) False
Answer 12
a) True
If we don’t use WHERE clause in a join, it will do a cartesian product.
13. Which type of JOIN is used to select all records from the right table and the matched records from the left table?
Choose one option
a) OUTER JOIN b) SELF JOIN c) RIGHT JOIN d) NATURAL JOIN
Answer 13
c) RIGHT JOIN
A RIGHT JOIN returns all records from the right table and the matched records from the left table. Unmatched left table records receive NULLs.
14. Given the tables employees and departments, which query will list all department names even if they have no employees?
Choose one option
a) LEFT JOIN b) SELF JOIN c) RIGHT JOIN d) INNER JOIN
Answer 14
c) RIGHT JOIN
SELECT departments.department_name, employees.name
FROM departments
RIGHT JOIN employees ON departments.id = employees.department_id;
The RIGHT JOIN returns all department names, including those with no associated employees.
15. Can multiple JOIN operations be used in a single SQL query?
Choose one option
a) True b) False
Answer 15
a) True
Multiple JOIN operations can be utilized in a single query to combine data from more than two tables.
16. In a SQL JOIN, what does the ON keyword do?
Choose one option
a) Specifies the columns for joining b) Specifies the conditions for joining c) Specifies the number of rows to return d) Specifies the order of columns
Answer 16
b) Specifies the conditions for joining
The ON keyword specifies conditions that determine which rows are retrieved in the join operation.
17. To combine data from multiple tables where relationships exist, which SQL feature should be used besides JOINs?
Choose one option
a) Subqueries b) Cursors c) Triggers d) Indexes
Answer 17
a) Subqueries
Subqueries can be used to combine data or perform operations that retrieve data from multiple tables based on conditions.
18. What is a NATURAL JOIN based on?
a) Explicit conditions b) Common columns automatically determined c) Primary keys d) Foreign keys
Answer 18
b) Common columns automatically determined
A NATURAL JOIN automatically joins tables based on columns with matching names and compatible data types.
19. Below two tables (A & B) are given.
What will be the result of inner join between these tables?
a) 1,3,4 b) 1,2,3,4,5,6,7,8,9,10,12 c) 1,2,3,4,5,6,7 d) 1,3,4,8,9,10,12
Answer 19
a) 1,3,4
20. What will be the result of FULL OUTER JOIN between these tables?
Choose one option
a) 1,3,4 b) 1,2,3,4,5,6,7,8,9,10,12 c) 1,2,3,4,5,6,7 d) 1,3,4,8,9,10,12
Answer 20
b) 1,2,3,4,5,6,7,8,9,10,12
21. Select the correct query/queries for cross join:
a) Select * FROM Table1 T1 CROSS JOIN Table1 T2;
b) Select * FROM Table1 T1 ALL CROSS JOIN Table1 T2;
c) Select * FROM Table1 T1,Table1 T2;
d) Select * FROM Table1 T1 CROSS Table1 T2;
Answer 21
a) Select * FROM Table1 T1 CROSS JOIN Table1 T2;
22. Which clause can be used with a JOIN to filter records?
a) WHERE b) HAVING c) GROUP BY d) ORDER BY
Answer 22
a) WHERE
The WHERE clause can be used to filter records after the JOIN operation, whereas HAVING is typically used with GROUP BY.
23. How can SQL JOINs be optimized for better performance?
a) Using indexes on join columns b) Avoiding WHERE clauses c) Using more complex joins d) Increasing database storage
Answer 23
a) Using indexes on join columns
Optimizing SQL JOIN performance generally involves using indexes on the columns involved in the join conditions.
24. Consider the tables `students` and `enrollments`, both having the column `student_id`. What will the following SQL statement do?
SELECT * FROM students NATURAL JOIN enrollments;
a) Join using all columns that are common between the two tables b) Require user to specify a join condition c) Join using columns named `student_id` d) Join with duplicate column names represented multiple times
Answer 24
c) Join using columns named `student_id`
The NATURAL JOIN will operate on all columns with the same name, joining on `student_id` as it’s the common column between `students` and `enrollments`.
25. What is a potential disadvantage of using NATURAL JOIN?
a) It is only available in a few SQL dialects b) Can join tables that have no related columns c) Lacks explicit control over join conditions d) Always results in Cartesian product of the tables
Answer 25
c) Lacks explicit control over join conditions
NATURAL JOIN does not allow you to specify which common columns to use for joining, which can lead to unforeseen results if multiple columns share the same name across tables.
We would love to hear from you! Please leave your comments and share your scores in the section below