Testing Insights

Common Pitfalls of Automated Testing POM: Four Anti-Pattern Analysis

2025-10-29

In the automated testing framework, we often use the POM (Page Object Model) pattern to encapsulate the page under test. However, in the actual development process, we often see that due to a poor understanding of POM or improper practices, the test code structure gradually deviates from its original intention, making the automated testing framework bloated and difficult to maintain.

This article will sort out the common anti-patterns of POM to help you better practice and implement POM patterns in your projects.

What is the Page Object Model (POM)?

POM is essentially a design pattern used to create a library of objects for web page elements. Its core purpose is to establish mechanisms that provide a more robust, stable and flexible design for the framework.

In the development of page object classes, we create a corresponding page class for different pages, each of which contains the element locators on that page and common operation methods.

This method realizes the decoupling of page elements from the test logic, and multiple test scripts can reuse elements and methods in the same page class, thereby greatly reducing code duplication and improving overall maintainability and scalability.

//img.enjoy4fun.com/news_icon/d40ntgtdm8bc72v66fq0.jpg

Why use POM

1. Maintainability

In the development process of web applications, if the test code directly manipulates the objects on the page, once the UI changes, all test cases may fail, resulting in scattered modifications and high maintenance costs.

Suppose there are multiple different login test cases (e.g., different usernames, wrong passwords, CAPTCHA verification, etc.). Once an element of the page changes, or if a validation box is added to the login page, all test cases involving logins need to be modified.

POM abstracts and encapsulates the page under test, bringing together page elements and how-to interaction logic in a class. When the UI changes, only the corresponding Page class needs to be modified to improve the maintainability of the test case code.

2. Code reusability

POM encapsulates the actions on the page so that different test scripts can reuse the same interaction logic, reducing code duplication.

For example, multiple test cases may require login functionality, and if each test is manually entered with a username and password, the code will be heavily redundant. However, with POM, the login() method can be encapsulated and directly called in different tests, improving code reusability.

3. Decouple the test logic from the UI structure

After using POM design, the concept of hierarchical design is indirectly introduced to the test framework, and the test logic of the upper layer is decoupled from the bottom page. This decoupling method allows the code at the test layer to focus on the test itself without relying on specific operations or elements.

Design principles of POM

1. Single Responsibility Principle (SRP): Each page object class should be responsible for only one page or a small part of the page's functionality. This keeps the code structured and easy to maintain, while also defining the boundaries of responsibilities across different functional areas.

2. Abstraction: Page objects should mask details of page interactions, such as element positioning and specific operation methods. By providing a more intuitive and readable interface, abstraction reduces the vulnerability of test code and improves its maintainability.

3. Encapsulation: Page objects should encapsulate the state and behavior of the page, allowing developers to easily understand the current state of the page and the actions that can be performed. This helps maintain a separation of concerns and improves the readability of your test code.

4. Reusability: Page objects should be designed to be reusable across multiple tests, reducing code duplication and improving the efficiency of test development. By building modular, self-contained page objects, test logic can be quickly assembled like building blocks.

5. Easy to Understand: The naming of methods and variables in page objects should have good readability and expressiveness, allowing developers to understand their functions without reading the underlying code. Clear naming helps improve the readability and maintainability of test code.

6. Separation of Concerns: Test scripts should focus on describing the high-level behavior of the page, while page objects are responsible for the underlying page operation details. Achieving this separation of duties allows for clearer and more organized test code, while building a more scalable testing framework.

Common Anti Patterns in POM

1. Add assertions or validation logic to the Page Object

The most common habit is probably to add assertions to the POM, such as directly writing in the login method to determine whether the login is successful. Because it seems that it can be reused, but wait a minute!

Including assertions in the page object method blurs the focus. The page object's responsibility should be limited to encapsulating element operations on the page, while assertions fall under the test validation logic and should be the responsibility of the test case. Writing assertions in page classes can lead to confusion of responsibilities and reduce the flexibility and maintainability of test cases.

Don't do this: assert on the page object.

//img.enjoy4fun.com/news_icon/d40ntrr8hlms72ok1lp0.png

Recommended practice: Place assertions in test cases。

//img.enjoy4fun.com/news_icon/d40nuhj8hlms72ok2bc0.png

The main problem with mixing assertion logic into the page object POM is that it brings:

1. Limit the expansion of use cases and affect multi-scenario testing

Take login as an example, when you need to test multiple login scenarios (success, wrong password, empty username, etc.), but the assertion is written dead in the login() method, causing you to have to copy the code or create multiple methods (e.g., login_with_valid_credentials(), login_with_invalid_password()), which in turn increases code duplication and maintenance costs.

2. Causing unnecessary failures

Let's say you have a testing process that is: create a user → delete a user. The focus of the test is to verify that the delete function is working properly, but if you add an assertion (validating the welcome message, etc.) in the "create user" step, once this verification fails unexpectedly (such as a delay), it will block the next step and make it impossible to verify the delete function you really want to test.

//img.enjoy4fun.com/news_icon/d40nupldm8bc72v67r7g.png

3. Introducing complex validation logic or external dependencies

Sometimes the verification results need to call interfaces, check databases, check back-end logs, etc., which belong to the category of test logic and should not be mixed into page classes. If you stuff this logic into the Page class, not only will the code be bloated, but it will also cause the page class to depend on the test environment, which violates the hierarchical design of the test framework.

//img.enjoy4fun.com/news_icon/d40nv30fe6kc72orfrgg.png

more stories
See more