Module 9

The Complete Anti-Pattern Gallery

A museum of testing mistakes. Study these exhibits carefullyβ€”recognizing what not to do is just as important as knowing what to do.

Museum gallery displaying framed anti-pattern warning signs
The Hall of Testing Shame: 13 patterns to avoid

Dear Marilyn: I inherited a test suite that everyone says is "unmaintainable." How do I know what is wrong with it?

β€” Inheritor in Indianapolis

Dear Inheritor: Walk through this gallery with me. I will show you the 13 most common anti-patterns in test automation. By the time we finish, you will be able to diagnose your test suite like a doctor reading an X-ray.

The 13 Anti-Patterns

πŸ”’

#1Hardcoded

Test data and element identifiers are embedded directly in the test code.

The Problem

Any change to the UI or data requires modifying the test itself.

BAD
click(element: "#btn_submit_v2")
enter_text(field: "txtUsername", value: "[email protected]")
GOOD
click(element: [Submit_Button])
enter_text(field: [Username_Field], value: [Test_User])
The Fix

Use interface definitions for elements and data sets for test data.

🍝

#2Spaghetti

Actions call other actions in a tangled web of dependencies.

The Problem

Impossible to understand the flow. Changes break unexpected tests.

BAD
action: process_order
  calls: validate_customer
    calls: check_credit
      calls: get_balance
        calls: validate_customer (circular!)
GOOD
action: process_order
  step: validate_customer()
  step: check_credit()
  step: create_shipment()
The Fix

Keep action hierarchies shallow. Actions should call down, never sideways or up.

πŸ”§

#3Klunky

High-level tests contain low-level implementation details.

The Problem

Business users cannot read the tests. Maintenance is difficult.

BAD
click(element: "#nav-menu")
wait(ms: 500)
click(element: "#menu-item-3")
wait(ms: 300)
click(element: "#submenu-2")
GOOD
navigate_to(page: "Invoice Management")
The Fix

Encapsulate low-level steps in mid-level actions. Keep high-level tests readable.

πŸ’€

#4Lifeless

Tests do not cover the complete lifecycle of a business object.

The Problem

Missing coverage for create, update, delete, or state transitions.

BAD
Test Module: Invoice
  - create_invoice βœ“
  - view_invoice βœ“
  - (no edit, delete, approve, reject tests)
GOOD
Test Module: Invoice
  - create_invoice βœ“
  - edit_invoice βœ“
  - delete_invoice βœ“
  - approve_invoice βœ“
  - reject_invoice βœ“
The Fix

Use the CRUD + State Transitions checklist for every business object.

🦿

#5Lame

Tests lack depthβ€”only happy path, no edge cases or error conditions.

The Problem

Bugs hide in the corners that are never tested.

BAD
Test: Create Invoice
  - Valid data β†’ Success βœ“
  (no negative tests)
GOOD
Test: Create Invoice
  - Valid data β†’ Success βœ“
  - Empty customer β†’ Error βœ“
  - Negative amount β†’ Error βœ“
  - Future date β†’ Warning βœ“
The Fix

For every positive test, add at least 2-3 negative tests.

❓

#6Clueless

Tests have no clear scope or objectiveβ€”they test 'everything' and 'nothing'.

The Problem

No one knows what the test is supposed to verify.

BAD
Test: General System Test
  Objective: "Test the system"
GOOD
Test: Invoice Calculation Accuracy
  Objective: "Verify that invoice totals are calculated correctly including tax, discounts, and multi-currency conversion"
The Fix

Every test module must have a specific, measurable objective.

πŸ₯·

#7Sneaky Checking

Verification logic is hidden inside actions instead of explicit check actions.

The Problem

Failures are hard to diagnose. Checks cannot be reused or skipped.

BAD
action: create_invoice
  (internally verifies total)
  (internally checks status)
  (throws if wrong)
GOOD
create_invoice()
check_invoice_total(expected: "$100")
check_invoice_status(expected: "Draft")
The Fix

Make all checks explicit. Actions do things; checks verify things.

πŸ’₯

#8Action Explosion

Hundreds of actions with minimal reuseβ€”each test has its own actions.

The Problem

Maintenance nightmare. No consistency across tests.

BAD
Actions: 500+
  - create_invoice_for_acme
  - create_invoice_for_globex
  - create_invoice_for_initech
  (each slightly different)
GOOD
Actions: 50
  - create_invoice(customer, amount)
  (parameterized, reusable)
The Fix

Parameterize actions. One action with arguments beats ten specialized actions.

πŸ€–

#9Techno

Action names use technical jargon instead of business language.

The Problem

Business users cannot understand or validate the tests.

BAD
execute_sql_insert_tbl_inv
post_http_api_v2_orders
trigger_onclick_btn_submit
GOOD
create_invoice
submit_order
click_submit_button
The Fix

Use verb_noun naming with business terminology. No SQL, HTTP, or DOM references.

♾️

#10Endless

Test cases are extremely long with dozens or hundreds of steps.

The Problem

Hard to debug. First failure masks all subsequent issues.

BAD
Test Case: Complete Order Flow
  Steps: 1-150
  (login β†’ browse β†’ cart β†’ checkout β†’ payment β†’ shipping β†’ confirmation β†’ ...)
GOOD
Test Case: Add to Cart (10 steps)
Test Case: Checkout (12 steps)
Test Case: Payment (8 steps)
The Fix

Break long tests into focused scenarios. Each test should verify one thing well.

πŸ”ͺ

#11Swiss Army Knife

A single action tries to do too many things based on parameters.

The Problem

Complex, fragile, and impossible to understand.

BAD
process_entity(
  type: "invoice|order|customer",
  operation: "create|update|delete|view",
  validate: true|false,
  notify: true|false
)
GOOD
create_invoice()
update_order()
delete_customer()
The Fix

One action, one purpose. Split multi-purpose actions into focused ones.

πŸ”

#12Over-Checking

Every single step has verification, creating noise and brittleness.

The Problem

Tests fail on irrelevant changes. Signal lost in noise.

BAD
click_menu()
check_menu_expanded()
click_item()
check_item_highlighted()
check_page_loading()
check_page_loaded()
check_title()
check_breadcrumb()
GOOD
navigate_to(page: "Invoices")
check_page_title(expected: "Invoice Management")
The Fix

Check outcomes, not intermediate states. Verify what matters to the business.

🧊

#13Fragile Locators

Element identification relies on brittle attributes that change frequently.

The Problem

Tests break with every UI update, even cosmetic ones.

BAD
click(element: "div.container > form > div:nth-child(3) > button.btn-primary")
GOOD
click(element: [Submit_Button])

# Interface Definition:
Submit_Button = "#btn-submit" OR "button[data-testid=submit]"
The Fix

Use stable identifiers (data-testid, IDs). Abstract locators into interface definitions.

Quick Diagnostic Checklist

Use this checklist to quickly assess a test suite for anti-patterns:

Are element IDs hardcoded in tests?
Do actions call other actions circularly?
Can a business user read the high-level tests?
Is CRUD coverage complete for each object?
Are there negative/error test cases?
Does each test module have a clear objective?
Are checks explicit (not hidden in actions)?
Is the action count reasonable (not exploding)?
Do action names use business language?
Are test cases under 20 steps each?
Does each action have a single purpose?
Are locators abstracted into definitions?

Module Summary

The 13 anti-patterns fall into three categories:

Structure Problems

Hardcoded, Spaghetti, Klunky, Fragile Locators

Coverage Problems

Lifeless, Lame, Clueless, Over-Checking

Design Problems

Sneaky Checking, Action Explosion, Techno, Endless, Swiss Army Knife