On this page10 sections

Selenium — Build a Page Object Framework

Design and implement a Selenium 4 + Java Page Object Model framework from scratch: BaseTest, page classes, explicit waits, and a TestNG suite with parallel execution.

Role

Java automation engineer

Difficulty

Intermediate

Time limit

2–3 hours

Category

ui automation

Selenium 4Java 17TestNGMavenWebDriverManager

Scenario

You have been asked to build a reusable Selenium 4 + Java test automation framework for a web application. Your team has chosen the-internet.herokuapp.com as the practice target — it exposes real UI interactions (login, dynamic controls, dropdowns) without requiring registration. The framework must be hand-offable: a new team member should be able to clone the repository, run `mvn test`, and get a passing suite with an HTML report — without reading the source. The exercise is not just about making tests pass; it is about building the structure that makes future tests cheap to add.

Requirements

  • 1.Create a Maven project with Selenium 4, TestNG 7, WebDriverManager, and a reporting dependency (ExtentReports 5 or Allure) declared in pom.xml
  • 2.Implement a BasePage class with a protected WebDriverWait field and at least four reusable helper methods: click(By), sendKeys(By, String), getText(By), and waitForVisible(By)
  • 3.Implement a BaseTest class that initialises the WebDriver before each test (reading the browser from a system property) and quits it after; use ThreadLocal<WebDriver> to support parallel execution
  • 4.Create at least three Page Object classes for the target application — each must declare locators as private static By fields and expose named action methods (e.g. enterUsername, clickLogin) rather than raw WebElement calls
  • 5.Write at least six TestNG test methods across at least two test classes, covering happy paths and at least two negative scenarios; use @DataProvider for at least one parametrised test
  • 6.Configure a TestNG XML suite file (testng.xml) that runs tests in parallel at the method level with at least two threads
  • 7.Ensure a clean `mvn test` run produces an HTML report (ExtentReports dashboard or TestNG's default emailable-report.html)

Starter data

  • Target application: https://the-internet.herokuapp.com (no account needed)
  • Login page: https://the-internet.herokuapp.com/login — valid credentials: username 'tomsmith', password 'SuperSecretPassword!'
  • Other useful pages: /dropdown (standard HTML select), /checkboxes (checkbox interaction), /dynamic_controls (element appear/disappear with explicit wait scenarios)
  • Maven archetype to start from: maven-archetype-quickstart — rename src/main/java to hold page objects and src/test/java for tests
  • Minimal pom.xml dependencies: selenium-java (4.x), webdrivermanager (5.x), testng (7.x), extentreports (5.x) — all available on Maven Central

Expected deliverables

  • A runnable Maven project (pom.xml + src/ tree) pushed to a public Git repository or delivered as a zip archive
  • BasePage.java and BaseTest.java in a shared base package with WebDriverWait-based helper methods and ThreadLocal driver management
  • At least three Page Object classes with private By locators and named action methods
  • At least two test classes with six or more @Test methods, including at least one @DataProvider
  • A testng.xml suite file that enables parallel method-level execution
  • An HTML test report generated by a clean `mvn test` run (screenshot of the report or the report file committed to the repo)
  • A README with a one-command run instruction: `mvn test -DsuiteFile=testng.xml`

Evaluation rubric

DimensionWhat reviewers look for
Page Object structureAre locators declared as private static By fields in the page class? Do page methods expose actions (clickLogin, enterUsername) rather than raw WebElement references? Are assertions kept out of page objects?
Wait strategyDoes BasePage use WebDriverWait with ExpectedConditions throughout? Is Thread.sleep absent from the codebase? Are waits applied before every interaction, not just on the first element?
BaseTest lifecycleIs the driver initialised in @BeforeMethod and quit in @AfterMethod? Is ThreadLocal used so that parallel test threads each hold their own driver instance?
Test qualityDo tests verify outcomes with assertions, not just execute actions? Is @DataProvider used with at least two meaningful data rows rather than a single value? Are negative scenarios (wrong credentials, invalid state) covered?
Parallel safetyAre page object instances created per test (not shared across threads)? Does the suite run successfully with thread-count >= 2 without race conditions or NullPointerExceptions?
Project organisationIs the Maven directory layout correct (src/main/java for base classes and pages, src/test/java for test classes)? Are page objects and tests in clearly named separate packages? Can a colleague run the suite from the README without asking clarifying questions?

Sample solution outline

  • pom.xml: declare selenium-java 4.x, webdrivermanager 5.x, testng 7.x, extentreports 5.x; set maven-surefire-plugin to use testng.xml via suiteXmlFiles
  • BasePage.java: constructor takes WebDriver; protected WebDriverWait wait (10 s default); methods: click(By), sendKeys(By, String), getText(By), isDisplayed(By), waitForUrlContains(String)
  • BaseTest.java: private static ThreadLocal<WebDriver> driverHolder; @BeforeMethod setupDriver reads System.getProperty('browser', 'chrome') and uses WebDriverManager to initialise; @AfterMethod quits and removes from ThreadLocal
  • LoginPage.java: BY_USERNAME, BY_PASSWORD, BY_SUBMIT, BY_FLASH_MSG; methods: enterUsername(String), enterPassword(String), clickLogin(), getFlashMessage() — all delegating to BasePage helpers
  • SecureAreaPage.java: BY_HEADING, BY_LOGOUT; methods: isSecureAreaDisplayed(), clickLogout()
  • DropdownPage.java: BY_SELECT; methods: selectByVisibleText(String), getSelectedOptionText() using WebDriverWait on the select element
  • LoginTest.java: testValidLoginRedirectsToSecureArea(); testInvalidLoginShowsError(@DataProvider with two invalid-credential rows: wrong password, wrong username)
  • DropdownTest.java: testSelectOption(@DataProvider for 'Option 1' and 'Option 2'); asserts selected text matches chosen option
  • testng.xml: <suite name='POM Suite' parallel='methods' thread-count='2'>; includes both test classes
  • ExtentReports setup in a TestNG ITestListener: onStart creates report, onTestSuccess/onTestFailure log result, onFinish flushes the HTML report to target/reports/

Common mistakes

  • Using Thread.sleep() instead of WebDriverWait — causes slow, brittle tests that fail unpredictably on different machines and network speeds
  • Declaring the WebDriver as a static (non-ThreadLocal) field — causes race conditions and NullPointerExceptions when parallel threads overwrite each other's driver reference
  • Putting assertions inside Page Object methods — obscures test intent and makes page objects impossible to reuse across tests with different expected outcomes
  • Calling WebElement methods directly in test classes instead of going through page object action methods — defeats the purpose of POM and couples tests to locator details
  • Not calling driver.quit() in @AfterMethod — browser processes accumulate, exhaust memory, and can cause subsequent tests to fail due to port conflicts
  • Hardcoding the browser type in BaseTest — the browser should be configurable via a system property or properties file so CI can switch browsers without a code change

Submission checklist

  • pom.xml lists Selenium 4, TestNG 7, WebDriverManager, and a reporting library
  • BasePage.java exists with WebDriverWait and at least four helper methods
  • BaseTest.java uses ThreadLocal<WebDriver> with @BeforeMethod/@AfterMethod lifecycle
  • At least three Page Object classes with private By fields and named action methods
  • At least six @Test methods across at least two test classes
  • @DataProvider used in at least one test with at least two meaningful data rows
  • testng.xml file runs tests in parallel (parallel='methods' or parallel='tests')
  • A clean `mvn test` run produces an HTML report without test failures
  • README contains a one-command run instruction

Extension ideas

  • +Add a screenshot-on-failure mechanism using a TestNG ITestListener that captures and embeds a screenshot in the ExtentReports output
  • +Replace hardcoded test data with a JSON or YAML file loaded at runtime so test inputs can be changed without modifying Java code
  • +Extend testng.xml to run the same suite against Chrome and Firefox in separate <test> nodes using a browser parameter — demonstrating cross-browser coverage