When it comes to test automation frameworks, adopting clear and concise standards and defining an easy-to-maintain, functional architecture is just as important as the service the project will provide.
According to the ISTQB Test Automation Engineer Syllabus (2016), a good practice is to separate the software used for testing from the system under test to minimize interference. This means that a test automation framework must be designed and managed as a standalone software product with a lifecycle of its own, with maintainability and ease of learning in mind. Doing so helps keep the normal and necessary maintenance tasks simple and straightforward and allows quality automation engineers to focus on designing better tests.
In this post, we will review 2 models for test automation framework design: Page Object Model and Page Component Object Model, focusing on how the latter can better facilitate maintenance, updates, and expansion when compared to the former.
Page Object Model
Keeping ease of maintenance front-of-mind, the most popular framework used for UI automated testing is the Page Object Model. In this framework, the areas that the UI test interacts with are modeled as objects within the test code. The Page Object Model framework reduces the amount of duplicated code and the number of places where fixes have to be made in case of a change in the UI. According to the Selenium Project, the advantages of the Page Object Model are:
- There is a clean separation between the test code and page-specific code, such as locators (or their use if you’re using a UI Map) and layout.
- There is a single repository for the services or operations the page offers rather than having these services scattered throughout the tests.
A Page Object Model example
Usually, a page that has been modeled is comprised of the following parts:
- Locators, to define components in the page
- Methods, that interact with these locators
- Single locator interaction
- Page functionality that chains single locator interactions
A simple login page can then be modeled as follows:
Each modeled page needs to provide means of interacting with locators defined on it (if the tests demand) and group single component actions into functionalities exposed to the tests. The Page Object Model then recommends encapsulating all common actions that can be applied to page components into a Base Page Class that all other pages will be inheriting from, where if any change in any of the methods is needed, these changes will be made in a single place.
In this example, the test automation engineer then has to map methods with locators on each modeled page. They also need to remember which actions can be performed on which type of locators on each of the pages. These actions are made easier with the help of an IDE.
Following the Page Object Model, the knowledge of the actions that can be performed on any of the page’s components belonging to the Page Class rests on the page itself; the components that comprise the page do not have information on the services they can expose.
Page Component Object Model
In many cases, it’d be helpful for the components themselves to define their own behavior and take ownership of the services exposed, allowing the test automation engineers to access their corresponding action methods. Using the Page Component Object Model framework, we can create a model where the components are modeled as objects that deliver services to the pages.
Components, as pages, can be modeled into a hierarchy where the base component will define methods that are common to all components. Some examples of these common methods include clicking on the component, validating text, checking for visibility, checking if it’s active or inactive, and all other actions that can be generally performed on any component regardless of type (methods that validate component status, if called from the test itself, do not break the Page Object Model paradigm of performing validations only inside the tests). Then, each component can further specialize behavior and expose it to the page.
A Page Component Object Model example
Let’s take a look at how an input component could be represented under the Page Component Object Model
The input component will be exposing Type, Clear. Also, all methods present on the base component to the Page class, which in turn allows the test to access these actions.
A Page Object, then, will be comprised of:
- Component Object initialization, using locators defined on the page to make each component addressable. This initialization is to be made in the Page constructor.
- Methods that interact with these components:
- Page functionality that chains single component interactions
A simple login page modeled in the test automation framework with a focus on components would look like this:
Now let’s say a test that makes use of this LoginPage object wants to type the string “bad username” into its usernameInput component for negative testing (supposing that the system does not allow a blank space in usernames and would show an error message). It would look like this:
And comparing it to how the same action would look in the traditional Page Object Model:
Advantages of the Page Component Object Model
- The test automation engineer does not have to create a new method for individual actions on each and every page object, thus reducing page object modeling efforts.
- The actions that each component makes public will be clearly made available to the test automation engineer across the UI, freeing mental space to design better test cases.
- The tests are easier to read and understand, facilitating maintenance and updates.
When using the Page Component Object Model, the SOLID principles should be taken into account:
- Single Responsibility: Each component only knows and exposes the services that matter to itself; it is only responsible for the tasks it can accomplish.
- Open/Closed: Creating a component hierarchy. I.e Base component <- Input Component.
- Liskov Substitution: Child component classes never narrow down on the parent component class’s behavior or overwrite the parent’s methods in a way that would cause errors.
- Interface Segregation: Interfaces can be managed by a single interface for the Base Class, allowing all components to be compatible with the framework.
- Dependency Inversion: As a page object is defined by the components it holds, it can be created by declaring each component using the concrete component type, but also by declaring components by the base class.
If there is an existing test automation framework in which the tests show flakiness, are difficult to maintain, have hardcoded waits (sometimes a necessary hack), or utilitarian methods on a single or in different helper classes, it can be easily migrated to the Page Component Object Model in order to get a clearer understanding of each of the pages’ component behavior (which could lead to getting rid of the hardcoded waits) and a better, simpler view of the automation framework in general.
The Page Component Object Model is a further abstraction of the Page Object Model, where the components themselves take a leading role in defining behavior exposed by the page and modeling the possible user behavior into the system.
By abstracting the page components themselves and using the page object as a component holder, maintenance and further development of the test automation framework tasks become easier to manage since each of the component’s behavior is centralized, easier to access, and clearer to understand. The Page Component Object Model makes it easier to uphold SOLID principles and keep the codebase clean, ordered, and easily visible for all test automation engineers involved.