Code should not only work—it should be understandable, modifiable, and robust. Design patterns help you reduce duplication, isolate change, and speed up maintenance. You can use them alone or mix them. In this section we will briefly introduce the most commonly used patterns. For further and more detailed material on the design patterns, we highly recommend the following website, which offers free and detailed informations: https://refactoring.guru/design-patterns
What are design patterns?
Design patterns are reusable ways to structure tests so they’re easier to read, cheaper to maintain, and safer to scale. The three main categories are:
- creational patterns:
- focuses on the mechanisms of object creation
- most common examples:
- Factory: The Factory Pattern is used when we want to create objects without directly calling their constructors.
- Builder: The Builder Pattern is used to create complex objects step by step, with different configurations.
- Singleton: Ensures only one instance of a class exists and provides a global access point.
- Prototype: Creates new objects by copying (cloning) existing ones instead of instantiating from scratch.
- Abstract Factory: Provides an interface for creating families of related objects without specifying their concrete classes.
- Structural Patterns
- Focus on how classes and objects are composed to form larger structures.
- Examples:
- Facade: Provides a simplified interface to a complex subsystem, hiding its inner complexity.
- Decorator: The Decorator Pattern dynamically adds new functionality to an object without modifying its code.
- Proxy: The Proxy Pattern provides a substitute or placeholder for another object to control access.
- Adapter: Converts one interface into another so that incompatible classes can work together.
- Composite: Treats individual objects and groups of objects uniformly by organizing them into tree structures.
- Bridge: Decouples abstraction from its implementation so both can vary independently.
- Behavioral Patterns
- Deal with object interaction and the delegation of responsibilities.
- Examples:
- Strategy: The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable at runtime.
- Command: The Command Pattern encapsulates a request as an object, allowing for parameterization of different requests, queuing, or logging of requests.
- Template: Defines the skeleton of an algorithm, allowing subclasses to redefine specific steps without changing its overall structure.
- Observer: Establishes a one-to-many dependency so when one object changes state, all its dependents are notified automatically.
- Chain of Responsibility: Passes a request along a chain of handlers until one processes it (or none do).
The most used automation pattern: Page Object Model (POM)
A structural pattern for UI tests where each page/screen of the application is represented by a page object. The page object encapsulates locators and user actions (e.g., login
, search
, addToCart
).
Why it exists
- UI changes break tests. Centralizing selectors and actions in one place minimizes fallout.
- Keeps tests readable (“go to Login → login → expect Welcome”), not a mess of selectors.
When to use
- Any project with more than a handful of UI tests.
- Apps where the UI will change over time.
- Teams that want clear separation between test intent and UI mechanics.
How it’s structured
- Page class: exposes actions (e.g.,
login(user, pass)
) and keeps locators private. - Tests: call high-level actions; assertions live in tests or in a small “screen model” layer if you prefer.
Strengths
- Maintenance: change one selector → fix many tests.
- Reuse: one login method used everywhere.
- Readability: test steps look like the business flow.
Weak points / pitfalls
- “God page objects” (thousands of lines). Split by area/feature.
- Putting assertions and business logic inside page objects (mixing responsibilities).
- Brittle selectors—prefer stable attributes/roles, not deep XPaths.
- Traditional Page Object Model In the traditional approach, each page is represented as a class that directly encapsulates its selectors and methods.
Most commonly used POMs (solution variations for the same problem):
- Traditional Page Object Model In the traditional approach, each page is represented as a class that directly encapsulates its selectors and methods.
- Page Factory Pattern The Page Factory Pattern abstracts element initialization—often initializing locators once (or lazily).
- Component (or Section) Object Model Here you extract common UI parts into reusable components (like a header) and use them within page objects.
- Screenplay Pattern In the Screenplay Pattern, you focus on the "actor" that performs tasks. This approach encapsulates actions (tasks) and questions (assertions).
- Fluent (Chainable) Page Object Model Fluent POM returns the object instance from its methods so you can chain calls together.