Pure reference catalog of the canonical object-model architecture patterns for test automation frameworks - Page Object Model (Fowler), Screenplay (Marcano/Palmer/Hill), Component Object, App Actions (Cypress idiom), Service Object, Repository, and Screen Object (the desktop/mobile sibling of Page Object covering Windows UIA, macOS XCTest, Linux AT-SPI, Appium / Espresso) - each with its canonical citation, when-to-use rules, refuse-to-mix anti-patterns, and a worked example. This is the architecture-tier reference - what each pattern *is* - not file-level style rules and not tool-specific configuration. Use when designing, reviewing, or migrating a test framework's object-model architecture.
66
83%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
This skill is a pure reference - no execution steps; it is the canonical catalog cited to determine "what good looks like" per pattern. The catalog complements test-code-conventions (which is file-level §1-§10) with the architecture-tier vocabulary.
Do not use this skill to:
playwright-testing, cypress-testing, etc.).framework-choice-advisor.Each pattern below states what it is: its definition, canonical citation, and load-bearing rules. Use the pattern selection matrix to choose one; the detailed per-pattern when-to-use rules and anti-pattern tables live in references/pattern-catalog.md.
Canonical source: Martin Fowler's PageObject definition (the bliki article is the cross-language canonical reference) + Selenium HQ documentation on Page Object Models.
Fowler's definition: "A page object wraps an HTML page, or fragment, with an application-specific API, allowing you to manipulate page elements without digging around in the HTML."
Selenium HQ's elaboration: "A page object is an object-oriented class that serves as an interface to a page of your AUT… There is a clean separation between the test code and page-specific code, such as locators."
The three load-bearing rules:
addToCart, submitOrder), not the DOM mechanic (clickButton, typeIntoField).Canonical source: Antony Marcano, Andy Palmer, and Jan Molak - Serenity BDD documentation on Screenplay; origin article Marcano, Palmer, Molak and Smart, "Page Objects Refactored: SOLID Steps to the Screenplay/Journey Pattern," which traces the approach to Marcano's 2007 conception.
The Screenplay vocabulary (Serenity BDD docs):
| Term | Definition |
|---|---|
| Actor | The user or system performing tasks. "In Screenplay we model actors who interact with an application in various ways to perform tasks that help them achieve their goals." |
| Ability | A capability that enables actors to perform tasks (e.g., BrowseTheWeb, CallAnApi). |
| Task | A higher-level domain concept that groups Interactions (e.g., Login, AddToCart). |
| Interaction | A low-level operation (click, type, fetch). |
| Question | A query about system state used in assertions (e.g., TheCartTotal.value()). |
Why Screenplay vs POM: Screenplay separates what the user does (Tasks, Interactions) from what the user can do (Abilities) from what the user observes (Questions). The result is a SOLID-aligned object model that survives UI refactors better than POM in large suites.
Canonical source: Selenium HQ docs (Page Components are part of the official POM extension) + practitioner consensus (testing-library, Storybook, Playwright Component Testing). Treated as a refinement of POM, not a competing pattern.
Definition: A Component Object is a Page Object scoped to a UI component (header, nav, form, modal, card) rather than a whole page. Where a page contains a re-used component (the navbar appears on every page), the Component Object models that component once; each Page Object that contains it composes it in.
Canonical source: Gleb Bahmutov - "Application Actions: Use Them Instead of Page Objects" (Cypress blog, 2019).
Definition: App Actions bypass the UI for setup steps by exposing application functions (Redux dispatches, store mutations, API calls) directly via cy.window().its('app') or equivalent. The test still asserts via the UI; only the Arrange phase is short-circuited.
Why App Actions vs POM: "Logging in" is not what the test is about - it's overhead. App Actions skip the login UI flow and inject a session directly, making the test 10× faster and removing flake from the login form.
Canonical source: Ruby on Rails / Java enterprise testing patterns + practitioner blog consensus. Refinement of POM for non-UI test layers.
Definition: A Service Object is the API-test equivalent of a Page Object - it wraps a remote service (REST endpoint, GraphQL query, gRPC method, message-queue producer) with a domain API the test consumes. Methods like cartService.addItem(sku, qty) rather than httpClient.post('/api/cart/items', { sku, qty }).
Canonical source: Martin Fowler's Repository pattern (originally for domain-driven design) adapted for test-data setup. Practitioner adoption in 2020+ test frameworks (factory libraries layer on top).
Definition: A Repository in test context is the data-access abstraction that hides the storage mechanism (DB, fixture file, factory call) behind a domain API: userRepo.createTestUser({ role: 'admin' }).
Canonical source: Martin Fowler's PageObject article - the current bliki entry defines it as an object that "wraps an HTML page, or fragment, with an application-specific API". The earlier name WindowDriver (Fowler, 2004) covered desktop GUI windows under the same encapsulation principle before the term migrated to web. The desktop / mobile community reuses the structurally-identical pattern under the name Screen Object (one class per logical screen, locators + actions encapsulated, no assertions inside). No single owner formally documents the rename - screen object is community-canonical across FlaUI, XCUITest, Appium / Espresso practitioner literature.
The mobile sibling is documented inside Google's Android testing guidance as Screen Robot (Jake Wharton - Instrumentation Testing Robots (2016)) and inside Square's mobile literature as well; both reproduce the same encapsulation contract.
The three load-bearing rules transfer unchanged from POM:
window.Title, element.IsEnabled, control-pattern state; the Screen Object exposes those via getters but does not verify them.login.SubmitsCredentials() returns MainScreen. Compile-time detection of broken workflows survives the migration from web POM to desktop Screen Object.login.SubmitsCredentials(creds) not login.LoginButton.Click(). Methods are named after the user-meaningful action - same vocabulary rule as POM.Bad (mechanical leakage into the test body - same shape as the web POM anti-pattern):
[StaFact]
public void Logs_in_with_valid_credentials() {
var window = _fx.App.GetMainWindow(_fx.Automation);
window.FindFirstDescendant(cf => cf.ByAutomationId("Username")).AsTextBox().Enter("alice@example.com");
window.FindFirstDescendant(cf => cf.ByAutomationId("Password")).AsTextBox().Enter("hunter2");
window.FindFirstDescendant(cf => cf.ByAutomationId("LoginButton")).AsButton().Invoke();
Assert.Equal("Invoices", _fx.App.GetMainWindow(_fx.Automation).Title);
}Good (Screen Object at the business layer):
[StaFact]
public void Logs_in_with_valid_credentials() {
var login = new LoginScreen(_fx.App.GetMainWindow(_fx.Automation));
var main = login.SubmitsCredentials("alice@example.com", "hunter2");
Assert.Equal("Invoices", main.Title);
}The mechanics live inside LoginScreen (constants for AutomationIds, retry-wrapped element fetches, SubmitsCredentials returns the next Screen Object). The test reads as a specification.
The patterns are not equally good for every project. The matrix:
| Pattern | Best for | Avoid for |
|---|---|---|
| POM | Page-oriented web SUT, 3-50 engineers, classic frameworks | Component-first React/Vue (use Component Object); Cypress (consider App Actions) |
| Screenplay | Large suites (200+ tests), multiple actor types, SOLID enthusiasts | Small projects (overhead exceeds benefit); teams allergic to dependency injection |
| Component Object | React/Vue/Svelte component-architected SUT, Storybook-integrated | Server-rendered traditional pages (use POM) |
| App Actions | Cypress + Redux/store-architected SUT, setup-heavy tests | Critical-path / smoke tests (must exercise UI); SUT without programmatic state API |
| Service Object | API / integration / contract tests with 5+ services | UI-only tests (no service calls); contract tests via schemathesis (the tool generates its own client) |
| Repository | Multi-data-source projects, DB + fixture + factory in one suite | Single-source projects (overhead exceeds benefit) |
| Screen Object | Desktop / mobile SUT through any accessibility-tree backend (UIA, XCTest, AT-SPI, Appium / Espresso) | Pure web SUT (use POM); pure API tests (use Service Object) |
| Anti-pattern | Why it fails |
|---|---|
| Mixing two object-model patterns in the same codebase | Engineers can't tell which to write; vocabulary drift accelerates |
| Inheritance hierarchies >2 levels deep (BasePage → AppPage → DomainPage → SpecificPage) | Depth-3+ chains break unpredictably on root-level changes (§A2) |
| Page / Component / Task / Service Objects holding mutable test data | Cross-test coupling; parallel-execution breakage |
Public getter-style methods that expose locators (get loginButton()) | Defeats encapsulation; locators leak into test code |
| Object-model methods that wait, retry, or handle SUT errors | Hides flakiness; tests pass when they should fail loudly |
| Object-model classes that import test-framework assertion libraries | Implies assertions are happening inside; smell for the no-assertion rule |
framework-choice-advisor (in the qa-process plugin).test-data-patterns (in the qa-test-data plugin, sister catalog).test-isolation-patterns (sister catalog).test-code-conventions §11 (step design).test-code-conventions (file-level companion).Canonical sources, each cited inline above at the rule it grounds:
desktop-test-strategy-reference (qa-desktop), test-code-conventions - sibling catalogs.