A lot of the frontend test automation tools using a UI (e.g. UFT, Katalon Studio), have a built-in object repository to store all the test objects needed to automate your tests. The main purpose of these object repositories is to reduce the possibility of object duplication and to simplify object maintenance. Automation libraries, such as e.g. Selenium, don’t have these built-in object repositories and that’s where the Page Object Model (POM) comes in. It’s a design pattern which is implemented as a Selenium best practice for reducing code duplication and enhancing test maintenance.
Automating an application is easy, but doing this in a maintainable way is not. If we are not careful when writing automated tests, this may result in a lot of maintenance when something changes to the Application Under Test (AUT). Consider the following example without the use of page objects:
At first glance, maintenance looks easy, but when the test suite grows and more scripts start using the same page elements, the maintenance will become more difficult. The actual problem with the above code is that there is no separation between the test script and the AUT’s locators or test objects. The locators are duplicated in all test scripts using the login screen, this means that whenever something changes to one of the locators or to the login procedure, it will result in maintenance of all the test scripts using the modified locators or the login procedure.
How will the Page Object pattern enhance maintenance in this case? In the Page Object pattern, the Page Objects are separate classes which represent the actual pages or screens of the application. A Page Object class will contain a series of objects, representing the application screen, and a series of methods, representing the features of that screen. This results in a clear separation of test code (code for executing tests) and page specific code (code for representing the locators and the features of that page).
This class or page object can be reused in different test scripts. When organizing our code like this, any minor change to the application will result only in the maintenance of the page object itself instead of all test scripts. Be aware that, major changes in the application might still result in maintenance on both test script and page object class. Another advantage of working with page objects, together with good naming conventions, is that tests become more readable.
How the page objects are implemented is quite flexible, but in order to reach the required maintainability there are a few basic rules to follow:
Depending on your needs or preferences you can implement your own flavors to this design patterns, as long as you keep in mind the basic rules. There is for example a variant where you define all the locators in an .xml file instead of implementing them within the code. Another interesting variation on the POM, which uses the benefits of method chaining, is called Fluent Page Object Model. To implement this we need to make sure that every method within the page object class returns either an instance of the current page (in case the action we implemented remains on the same page) or the next page (in case the action we implement goes to another page). This way we can chain all our actions one after another, which will make the code more readable. The downside of this approach is that debugging becomes a bit more complicated.
Over the past years, the page object pattern has become very popular among Selenium users for automating web applications, with several variations as a result. This model is most often used in combination with Selenium for web automation, but it can also be used for mobile or desktop automation. When following the rules of the page object design pattern you should end up with more readable and robust tests, less duplicate code and enhanced maintainability.