The Journey pattern - the successor to PageObject

“The Journey pattern” supplants PageObject as the go-to UI automation pattern. This post explains why you should care and how you can start upgrading your PageObjects tomorrow.

 The home favourite: the PageObject pattern

The Page Object pattern has become the de-facto UI automation pattern - even the official Selenium docs include a description. The idea is to separate your test code into classes corresponding to each different screen (i.e. ‘Page’), with one method for each interaction with the page. To simplify the example from the docs, the following code

driver.findElement(By.cssSelector("#username")).sendKeys("myUsername");
driver.findElement(By.cssSelector("#password")).sendKeys("myPassword");
driver.findElement(By.cssSelector("#loginButton")).click();

becomes

new LoginPage(driver).loginAs("myUsername", "myPassword");


// PageObject
public class LoginPage {
    private WebDriver driver;
    private By usernameTextbox = By.cssSelector("#username");
    private By passwordTextbox = By.cssSelector("#password");
    private By loginButton = By.cssSelector("#loginButton");

    public LoginPage(WebDriver driver) {
        this.driver = driver;
    }

    public void loginAs(string username, string password) {
        driver.findElement(usernameTextbox).sendKeys(username);
        driver.findElement(passwordTextbox).sendKeys(password);
        driver.findElement(loginButton).click();
    }
}

This approach has some key maintainability advantages over the original:

 Chinks in the armour

However it’s not all unicorns and rainbows: these benefits aren’t free, and this is what we traded-off for them:

And we also left some problems unsolved:

 The contender: the Journey pattern

In my modified version of the pattern (which loses some concepts from the original), test code is separated into a number of different ‘concerns’ for increased maintainability:

// Goal - to verify login works (assertions omitted)
LogIn.as(driver, "myUsername", "myPassword");


// Task:
public class LogIn {
    public static void as(WebDriver driver, string username, string password) {
        Enter.textInto(driver, username, LoginScreen.usernameBox);
        Enter.textInto(driver, password, LoginScreen.passwordBox);
        Click.on(driver, LoginScreen.loginButton);
    }
}

// Screen:
public class LoginScreen {
    public static By usernameBox = By.cssSelector("#username");
    public static By passwordBox = By.cssSelector("#password");
    public static By loginButton = By.cssSelector("#loginButton");
}

// Actions:
public class Enter {
    public static void textInto(WebDriver driver, string text, By selector) {
        driver.findElement(selector).sendKeys(text);
    }
}

public class Click {
    public static void on(WebDriver driver, By selector) {
        driver.findElement(selector).click();
    }
}

 A sharper edge

 Youthful inexperience

 A new champion

The Journey pattern helps separate out the concerns of UI testing much better than pure PageObjects, so scales much better. It also lends itself well to an incremental transition from PageObjects - one-by-one extracting tasks and actions until all you’re left with are screens!

So have I convinced you - will you be re-writing your Page Objects tomorrow? Are there any other patterns you’ve seen that scale well for UI automation?

 
212
Kudos
 
212
Kudos