Skip to content

Modus-Logo-Long-BlackCreated with Sketch.

  • Services
  • Work
  • Blog
  • Resources

    OUR RESOURCES

    Innovation Podcast

    Explore transformative innovation with industry leaders.

    Guides & Playbooks

    Implement leading digital innovation with our strategic guides.

    Practical guide to building an effective AI strategy
  • Who we are

    Our story

    Learn about our values, vision, and commitment to client success.

    Open Source

    Discover how we contribute to and benefit from the global open source ecosystem.

    Careers

    Join our dynamic team and shape the future of digital transformation.

    How we built our unique culture
  • Let's talk
  • EN
  • FR

The most successful test design pattern is by far the Page Object pattern for enhancing test maintenance and reducing code duplication. A page object is an object-oriented class that serves as an interface to a page of your automation project. The tests use the methods of this page object class whenever they need to interact with the UI of that page. The benefit is that if the UI changes for the page, the tests themselves don’t need to change, only the code within the page object needs to change. Subsequently all changes to support that new UI are located in one place.1


NEW RESEARCH: LEARN HOW DECISION-MAKERS ARE PRIORITIZING DIGITAL INITIATIVES IN 2024.

Get Report


WebdriverIO – Page Object advanced concepts

Webdriver also provides support for the Page Object pattern. The goal is to abstract any page information away from the actual tests.

Main Page Object

You need to declare a main page object that will contain all the general selectors and functions. This will be inherited by all the other page objects.

Below is an ES6 example of a root page object:

"use strict";
class Page {
 constructor() {
 }
 open(path) {
   browser.url('/' + path);
 }
}
module.exports = Page;

Pages into Page Objects

Now that you have the main page object you can start creating the rest of the objects. Let’s use WebdriverIO as the app under test for our example.
First let’s create a page object for WebdriverIO home page. This is how the page might look:

let Page = require('./page');
class HomePage extends Page {
 get search() { return browser.element('//div[@id="docsearch"]//input'); }
 get searchResult() {return browser.element('//div[@class="algolia-docsearch-suggestion--wrapper"]'); }
 get title() { return browser.element('//header/h1'); }
 get description() { return browser.element('//header/h2'); }
 open() {
   super.open('');
 }
 doSearch(searchCriteria) {
 this.search.setValue(searchCriteria);
 browser.pause(3000);
}
 goToSearchResult() {
 this.searchResult.click();
}
 getTitle() {
   this.title.getText();
 }
 getDescription() {
   this.description.getText();
 }
}
module.exports = new HomePage();

Let’s create the second page object for guide page. This is how the page might look:

let Page = require('./page');
class GuidePage extends Page {
 get search() { return browser.element('//div[@id="docsearch"]//input'); }
 get searchResult() {return browser.element('//div[@class="algolia-docsearch-suggestion--wrapper"]'); }
 open() {
   super.open('guide.html');
 }
 doSearch(searchCriteria) {
 this.search.setValue(searchCriteria);
 browser.pause(3000);
}
 goToSearchResult() {
 this.searchResult.click();
}
}
module.exports = new GuidePage();

Identify the problem

Everything seems to look great and we’ve successfully implemented the Page Object pattern across our test project, but we may have missed something. We have some duplication of code due to the presence of the same navbar on both Home and Guide pages.

These kinds of situations occur frequently in real life testing so we need a solution. My suggestion is to uncouple the common section into separate Page Objects that we can also call Page Components and use Multiple Inheritance.

The solution – Page Components and Multiple Inheritance

There are a lot of topics and approaches on ES6 Multiple Inheritance. I propose using the xmultiple npm package to handle mixins.

Refactoring the above code gives us the following

"use strict";
class Page {
 constructor() {
 }
 open(path) {
   browser.url('/' + path);
 }
}
module.exports = Page;

"use strict";
class NavigationBarSection {
 constructor() {
 }
 get search() { return browser.element('//div[@id="docsearch"]//input'); }
 get searchResult() {return browser.element('//div[@class="algolia-docsearch-suggestion--wrapper"]'); }
 doSearch(searchCriteria) {
   this.search.setValue(searchCriteria);
   browser.pause(3000);
 }
 goToSearchResult() {
   this.searchResult.click();
 }
}
module.exports = NavigationBarSection;

"use strict";
let mixin = require('xmultiple');
let Page = require('./page');
let NavigationBar = require('./sections/navigationBar.section');
class HomePage extends mixin(Page, NavigationBar) {
 get title() { return browser.element('//header/h1'); }
 get description() { return browser.element('//header/h2'); }
 open() {
   super.open('');
 }
 getTitle() {
   this.title.getText();
 }
 getDescription() {
   this.description.getText();
 }
}
module.exports = new HomePage();

"use strict";
let mixin = require('xmultiple');
let Page = require('./page');
let NavigationBar = require('./sections/navigationBar.section');
class GuidePage extends mixin(Page, NavigationBar) {
 open() {
   super.open('guide.html');
 }
}
module.exports = new GuidePage();

What we did was to extract the common section into a different class. After this I used the multiple inheritance capability of the xmultiple library to provide my new objects with the necessary implementation.

Now we have clean code to handle our testing.

Conclusion

It seems easy at the beginning to implement the Page Object pattern into a WebdriverIO test project. However, a closer look will reveal the complexity of the problem and the necessity of applying different approaches according to the situation.

There are many ways to handle multiple inheritance in your WebdriverIO project. If this does not satisfy your needs you can still use the JavaScript Subclass Factories approach. Regardless of the approach used, the key to a successful automation project is to keep your code clean, and more importantly, to keep your page objects cleaner.

Here you can find the full working example used in this article.

References:


  1. Source: http://docs.seleniumhq.org/docs/06_test_design_considerations.jsp#page-object-design-pattern ↩
Posted in Application Development, Quality Assurance
Share this

Sergiu Popescu

Sergiu Popescu is a QA Engineer at Modus Create. He specializes in automating test processes for web and hybrid apps using Java, JS, and a wide range of tools and libraries like TestNG, jUnit, Webdriver, WebdriverJS, Protractor and Siesta. When he is not hunting bugs in apps, he enjoys spending time with his lovely wife and son.

Related Posts

  • Using the Page Object Design Pattern in Sencha Test
    Using the Page Object Design Pattern in Sencha Test

    Don’t Repeat Yourself (DRY) is a software development principle that is equally important to automation…

  • Using the Page Object Design Pattern in Sencha Test
    Using the Page Object Design Pattern in Sencha Test

    Don’t Repeat Yourself (DRY) is a software development principle that is equally important to automation…

Want more insights to fuel your innovation efforts?

Sign up to receive our monthly newsletter and exclusive content about digital transformation and product development.

What we do

Our services
AI and data
Product development
Design and UX
IT modernization
Platform and MLOps
Developer experience
Security

Our partners
Atlassian
AWS
GitHub
Other partners

Who we are

Our story
Careers
Open source

Our work

Our case studies

Our resources

Blog
Innovation podcast
Guides & playbooks

Connect with us

Get monthly insights on AI adoption

© 2025 Modus Create, LLC

Privacy PolicySitemap
Scroll To Top
  • Services
  • Work (old)
  • Blog
  • Resources
    • Innovation Podcast
    • Guides & Playbooks
  • Who we are
    • Careers
  • Let’s talk
  • EN
  • FR