Simulating a Page Object in Plain Java

An In-Memory DOM

There is no browser here, so this program simulates one: a FakeDriver holds a map from locator to element. The page classes are written exactly like real POM classes, so you can see how tests, pages and the driver talk to each other.

Example

LoginPage hides the locators; the test only calls page methods.

import java.util.*;

public class Main {
    public static void main(String[] args) {
        FakeDriver driver = new FakeDriver();
        HomePage home = new LoginPage(driver).loginAs("[email protected]", "Secret@123");
        System.out.println("Welcome text: " + home.welcomeText());
        System.out.println("Test " + (home.welcomeText().equals("Welcome, Asha") ? "PASSED" : "FAILED"));
    }
}

class FakeElement {
    String value = "";
    String text;
    FakeElement(String text) { this.text = text; }
}

class FakeDriver {
    private final Map<String, FakeElement> dom = new LinkedHashMap<>();
    FakeDriver() {
        dom.put("#email", new FakeElement(""));
        dom.put("#password", new FakeElement(""));
        dom.put("#login-btn", new FakeElement("Login"));
        dom.put("#welcome", new FakeElement(""));
    }
    FakeElement find(String locator) {
        FakeElement e = dom.get(locator);
        if (e == null) throw new IllegalStateException("NoSuchElement: " + locator);
        return e;
    }
    void type(String locator, String text) {
        System.out.println("  type  " + locator + " = " + (locator.equals("#password") ? "******" : text));
        find(locator).value = text;
    }
    void click(String locator) {
        System.out.println("  click " + locator);
        boolean ok = find("#email").value.equals("[email protected]") && find("#password").value.equals("Secret@123");
        find("#welcome").text = ok ? "Welcome, Asha" : "";
    }
}

class LoginPage {
    private final FakeDriver driver;
    private final String email = "#email", password = "#password", loginBtn = "#login-btn";
    LoginPage(FakeDriver driver) { this.driver = driver; }
    HomePage loginAs(String user, String pwd) {
        driver.type(email, user);
        driver.type(password, pwd);
        driver.click(loginBtn);
        return new HomePage(driver);
    }
}

class HomePage {
    private final FakeDriver driver;
    HomePage(FakeDriver driver) { this.driver = driver; }
    String welcomeText() { return driver.find("#welcome").text; }
}

Output

  type  #email = [email protected]
  type  #password = ******
  click #login-btn
Welcome text: Welcome, Asha
Test PASSED

What Happens When a Locator Changes

Example

The developer renames #login-btn to #sign-in. Only the page class knows the locator, so only one line must change. Here the page is still old, so the lookup fails:

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Map<String, String> dom = new LinkedHashMap<>();
        dom.put("#email", "input");
        dom.put("#password", "input");
        dom.put("#sign-in", "button");          // new id from the developer

        String loginButtonLocator = "#login-btn"; // LoginPage still has the old id
        for (String locator : List.of("#email", "#password", loginButtonLocator)) {
            if (dom.containsKey(locator)) {
                System.out.println("found   " + locator);
            } else {
                System.out.println("FAILED  NoSuchElementException: Unable to locate element: " + locator);
                System.out.println("Fix: change one field in LoginPage, all 120 tests pass again");
            }
        }
    }
}

Output

found   #email
found   #password
FAILED  NoSuchElementException: Unable to locate element: #login-btn
Fix: change one field in LoginPage, all 120 tests pass again

Practice

Exercise: Add a SearchPage to the simulation with a method searchFor(String text) that types into #search and returns the number of matching products from a fixed list.

Show answer
class SearchPage {
    private final FakeDriver driver;
    private final List<String> catalog = List.of("Boat Headphones", "Sony Headphones", "Mi TV");
    SearchPage(FakeDriver driver) { this.driver = driver; }
    long searchFor(String text) {
        driver.type("#search", text);   // add "#search" to the fake DOM first
        return catalog.stream().filter(n -> n.toLowerCase().contains(text.toLowerCase())).count();
    }
}