Skip to main content

Search...

Why Test Automation Needs Design Patterns

Test automation code deserves the same design care as production code. Page Object, Builder, Facade and why skipping design patterns turns frameworks into spaghetti.

8 min read
Cover for Why Test Automation Needs Design Patterns

Design patterns in test automation are proven, reusable solutions that keep test code structured, readable, and extensible. Key patterns include Page Object, which encapsulates web page elements into classes; Builder, which separates object creation from representation; and Facade, which provides a single entry point to grouped functionality. Supporting principles include DRY (do not repeat yourself), KISS (keep it simple), and YAGNI (you aren’t gonna need it).

Key Takeaways

  • Design patterns give test automation teams a shared vocabulary, so a problem can be solved by naming a pattern (Builder, Singleton, Facade) rather than re-explaining the solution from scratch.
  • Spaghetti test code is the direct result of skipping encapsulation: without patterns like Page Object, selectors and methods scatter across tests and duplication multiplies.
  • The YAGNI principle applies to test automation frameworks: implement only what is needed now, and leave extensibility as a structural option, not a pile of unused helpers.
  • AI tools will generate overcomplicated test automation code without explicit constraints in the prompt, and a human engineer must still verify and redesign the output.
  • The Gang of Four design patterns, published in 1994, remain directly applicable to test automation, including creational patterns and the Pool Object pattern for managing parallel web driver instances.

Why design patterns belong in test automation code

Test automation code is real code, and it deserves the same care as the production system it checks. Kostiantyn Teltov has spent nearly two decades in software testing with a technical background across C#, Java, JavaScript, TypeScript, and Python. His position is direct: take care of your test automation code, because it grows and outlives the quick prototype you started with.

Design patterns serve four jobs here. They give you a proven solution instead of a reinvented wheel. They enable object-oriented principles, which keeps the code from turning into spaghetti. They create a shared vocabulary in the team. And they support extensibility, so the small framework you build today can scale tomorrow.

That last point is the practical payoff. A test suite that starts as a prototype often becomes the backbone of a larger solution. Patterns are what let it grow without collapsing under its own weight.

What the Page Object pattern solves

The Page Object is the most common pattern in UI test automation, and it exists to stop duplication. Without it, you write all your selectors and methods directly inside your tests, and you repeat them every time you touch the same screen.

A Page Object encapsulates a screen or a web page in a class. If the page is large, you split it into subclasses for the smaller parts. The selectors and the web driver calls live in one place, and the tests call into them.

This works regardless of programming language or test framework. Page Objects are a shared baseline that almost every UI automation engineer reaches for first, which is exactly why they are the natural entry point into pattern thinking.

Builder, Facade, and Pool: three patterns worth knowing

Beyond Page Objects, a handful of patterns earn their place in automation work. Kostiantyn singles out the Builder and the Facade, and points to a non-Gang-of-Four pattern, the object pool, for a specific performance problem.

The Builder separates object creation from object representation. The analogy is a burger: from one constructor you build a cheeseburger or a hamburger by assembling the parts you want. In tests this reads cleanly. Without adopting a full BDD framework like Cucumber or SpecFlow, a Builder makes test setup almost as readable, because the construction steps spell out what the test needs.

The Facade works like a reception desk. You arrive at one class, and from there you reach whatever you need. For an API, a Facade lets you navigate to users, customers, or orders without repeating the same initialization inside every test. You set up the similar classes once, the REST request builders for example, and the tests go through the single entry point.

The object pool addresses memory churn. Imagine creating web drivers in parallel, then closing them, again and again, with memory spiking each time. Instead of creating and destroying on every run, you build a pool, take a driver from it, return the driver to it, and clean up the pool at the end. The idea is simple once someone shows it to you.

Here is a quick orientation on what each pattern is for:

PatternProblem it solves
Page ObjectDuplicated selectors and web driver calls inside tests
BuilderVerbose object setup, hard-to-read test data construction
FacadeRepeated initialization and navigation across many classes
Object poolMemory churn from creating and destroying objects repeatedly

The Gang of Four patterns still hold up

Patterns published in 1994 remain valid decades later. The Gang of Four creational patterns are a good place to start: singleton, factory method, abstract factory, builder, and prototype.

Not all of them carry the same weight today. Prototype, for instance, matters less in modern languages, because you can already copy objects under the hood without it. The others still find regular use, depending on what you are building.

Two design principles sit alongside the patterns. The dependency inversion principle, implemented through dependency injection, is one Kostiantyn calls out as important for automation frameworks. The point is not to apply everything, but to know the catalogue well enough to pick what the situation needs.

DRY and KISS keep test code honest

Do not repeat yourself is the principle that ties most of these patterns together. A Page Object is DRY in action: you stop repeating element lookups and web driver methods by encapsulating them. The same logic extends to assertions you reuse or database calls you make often. Anything that starts to look like spaghetti is usually a repetition you can move into a helper.

Keep it simple guards the other flank. You are not designing the production project. You are designing tests for it, and you do not write tests for your tests. Reach for a pattern only when the problem calls for one, not because you just learned it and want to use it.

“Sometimes when, especially you learn something new, like a new design pattern, you are trying to implement it. But you always need to ask yourself, okay, if it’s suitable here, because sometimes we want to overcomplicate or overdesign.” — Kostiantyn Teltov

You aren’t gonna need it rounds out the set. Build what you need now. Design the framework so it can extend, but do not write helpers for use cases that do not exist yet. Premature scaffolding is its own form of clutter.

How AI handles patterns, and where it overcomplicates

AI can produce pattern-based code, but only if you give it the context, and you still have to verify the result. AI works from prompts, so a pattern appears in the output only when you ask for it. Leave the context out, and it won’t apply one.

The verification matters more than the generation. Kostiantyn prepared a workshop that generated a Playwright framework from scratch with ChatGPT, and during preparation it worked well. A month later, running the same session live on a paid plan, the tool started to overcomplicate, stacking conditional after conditional. He had to redesign the output by hand and tell participants outright that the generated version was not correct.

The lesson is control, not avoidance. AI keeps getting more capable, but the engineer still decides whether the generated code is appropriate or bloated. Knowing the patterns yourself is what lets you make that call.

From copy-paste to fluent: how to actually learn patterns

Most automation engineers start by copying. You join a team, your senior colleagues hand you frameworks and templates, and you copy-paste your way through the early work. That is a normal beginning, not an end state.

The path forward runs through three areas: object-oriented programming where it is required, data structures, and design patterns. Many automation engineers grew out of manual testing rather than development, so these foundations are not always there yet, and they are worth building deliberately.

For sources, Kostiantyn points to the original Gang of Four book on design patterns, the Refactoring Guru website with its illustrated explanations across multiple languages, and free material on YouTube, plus platforms like LinkedIn Learning and Udemy. Reading still pays off, even when long-form reading has fallen out of fashion.

The method that worked for him was social. His team met once a week in the office, prepared short presentations, and then solved practical tasks together using a given pattern. Finding colleagues who push you turns out to be as useful as any book.

Quality knowledge spreads when teams break their bubbles

Patterns stick faster when an organization shares them across teams. At his workplace, Kostiantyn helped start a QA Guild, the first guild in the organization, set up after he made the case to HR.

The guild had a clear purpose: spread knowledge and break bubbles. Each team can work differently and lean on its own approaches, and those silos keep good practices from traveling. A guild is a channel for moving them.

This connects back to one of the four reasons for patterns in the first place. A shared vocabulary only works if it is actually shared. When a teammate hits a problem, being able to say “resolve this with a Builder” or “use a Singleton here” only helps if both sides speak the same language, and a guild is how that language reaches the whole organization.

Share this page