DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Prototype Pattern in JavaScript
  • Design Pattern: What You Need to Know to Improve Your Code Effectively
  • What’s New in the Latest Version of Angular V15?
  • Ultimate Guide to FaceIO

Trending

  • Enhancing Business Decision-Making Through Advanced Data Visualization Techniques
  • Using Python Libraries in Java
  • Manual Sharding in PostgreSQL: A Step-by-Step Implementation Guide
  • Can You Run a MariaDB Cluster on a $150 Kubernetes Lab? I Gave It a Shot
  1. DZone
  2. Coding
  3. Languages
  4. Selenium: Design Patterns

Selenium: Design Patterns

By 
Łukasz Rosłonek user avatar
Łukasz Rosłonek
·
Sep. 05, 15 · Interview
Likes (5)
Comment
Save
Tweet
Share
10.0K Views

Join the DZone community and get the full member experience.

Join For Free

Selenium WebDriver is widely used as a first-choice framework when it comes to testing web applications. In this article I would like to introduce you to two design patterns, that work great with selenium tests. In this post we will dig into Page Object pattern and combine it with Page Factory. As always, whole code for project is available on my github.


Page Object Pattern

Page Object is a concept that helps to reduce the number of duplicated code, and helps with test maintenance, resulting from changes in the structure and functionality of the web application. The main idea behind page object is to place the code relating to the functionality of subpages in separate classes. In a very simplified way, if your web application includes pages:

  • home
  • about
  • contact

We should create three separate page object classes:

  • Home.java
  • About.java
  • Contact.java

Each class should contain only those methods that support functionality for the corresponding subpage, and define selectors only for this subpage. We should also remember, that public methods in page object class are only those, that represents user’s workflow.

One important point is that each public method in page object class should return object of page that user is on. For example, if a button on the page does not gets you to different subpage, this method should return this. Moreover, if a button is a link to another page, the method should return page object class of this subpage. Here is a code snippet of this approach using an example from previous article, where we wrote tests for Facebook login:

public class LoginPage {

    private static By userEmailLoginInput = By.id("email");
    private static By userPasswordLoginInput = By.id("pass");
    private static By loginSubmitBtn = By.id("u_0_n");

    private WebDriver driver;

    public LoginPage(WebDriver driver) {
        this.driver = driver;
        driver.get("http://facebook.com");
    }

    public LoginPage enterUserLogin(String login) {
        WebElement emailBox = driver.findElement(userEmailLoginInput);
        emailBox.click();
        emailBox.sendKeys(login);
        return this;
    }

    public LoginPage enterUserPassword(String password) {
        WebElement passwordBox = driver.findElement(userPasswordLoginInput);
        passwordBox.click();
        passwordBox.sendKeys(password);
        return this;
    }

    public HomePage submitLoginCredentials() {
        WebElement submitBtn = driver.findElement(loginSubmitBtn);
        submitBtn.click();
        return new HomePage(driver);
    }
}

In above example, methods enterUserLogin() and enterUserPassword() don’t transfer user to another subpage, but perform activities on the login page, so return type is object of LoginPage class (this). On the other hand, submitLoginCredentials() method moves user to home page (or to the page informing about the login failure), so it returns home page class object. In real life example, in HomePage.class we would have had methods that perform actions on the home page, but since it’s only an example code demonstrating pattern usage, we have only checkIfLoginSucceed() method here:

public class HomePage {

    private WebDriver driver;

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

    public boolean checkIfLoginSucceed() {
        return driver.getPageSource().contains("fbxWelcomeBoxName");
    }
}

…and test looks as follows:

@Test
public void shouldNotLoginWithIncorrectCreds() {
    LoginPage loginPage = new LoginPage(driver);
    loginPage.enterUserLogin("wrong@login.com");
    loginPage.enterUserPassword("wrongPassword");
    HomePage homePage = loginPage.submitLoginCredentials();
    assert (!homePage.checkIfLoginSucceed());
}


Page Factory pattern

The main idea behind this pattern is to support page object classes, and to allow better page selectors management. Page Factory provide us with a set of annotations, which work great with selectors and enhance code readability. To understand this, let’s look at a standard initialization webelement through the selector:

private static By email = By.id(“email”);
WebElement emailBox = driver.findElement(email);

Unfortunately, the readability leaves much to be desired. The problem is also that variable with selector and web element object require a separate initialization. Along with Page Factory, this is greatly simplified:

private WebElement email;

That’s all! Page Factory search page source code for element with id=”email” by default and assigns it to declared webelement. Of course, this minimalism can introduce some confusion, and therefore I recommend the use of @FindBy annotation:

@FindBy(id = “email”)
private WebElement userEmailLoginInput;

We can also search selectors by other attributes, such as xpath, name, className, etc. Everything we have to do to use this pattern, is to initialize PageFactory in the page object class constructor:

PageFactory.initElements(driver, this);

Our LoginPage.class with page factory pattern will look like this:

public class LoginPage {

    @FindBy(id = "email")
    private WebElement userEmailLoginInput;

    @FindBy(id = "pass")
    private WebElement userPasswordLoginInput;

    @FindBy(id = "u_0_n")
    private WebElement loginSubmitBtn;

    private WebDriver driver;

    public LoginPage(WebDriver driver) {
        this.driver = driver;
        driver.get("http://facebook.com");
        PageFactory.initElements(driver, this);
    }

    public LoginPage enterUserLogin(String login) {
        userEmailLoginInput.click();
        userEmailLoginInput.sendKeys(login);
        return this;
    }

    public LoginPage enterUserPassword(String password) {
        userPasswordLoginInput.click();
        userPasswordLoginInput.sendKeys(password);
        return this;
    }

    public HomePage submitLoginCredentials() {
        loginSubmitBtn.click();
        return new HomePage(driver);
    }
}

Important thing to notice is that every time we call a method on web element, page factory search for our element all over again through page sources. If page isn’t AJAX-based, we can use page factory cache to search an element only with page factory initialization, and then retrieve it from cache:

@FindBy(id = “email”)
@CacheLookup
private WebElement userEmailLoginInput;


Summary

As in every aspect of software development, design patterns in test automations help us to develop tests faster and more efficient. Page Object and Page Factory are two simple patterns, that significantly improve maintenance and readability of selenium tests. For complete sources of examples in this article please visit my github. If you have any questions, feel free to leave a comment!


Object (computer science) Design Factory (object-oriented programming) Web application

Opinions expressed by DZone contributors are their own.

Related

  • Prototype Pattern in JavaScript
  • Design Pattern: What You Need to Know to Improve Your Code Effectively
  • What’s New in the Latest Version of Angular V15?
  • Ultimate Guide to FaceIO

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!