Skip to content

Refactor a Test Suite for DRY Using AI

Updated 2026-06-08·intermediate·Test Code Review

Scans a set of test files and identifies duplicated setup, fixture state, and assertion patterns — proposes refactors using Playwright fixtures, factory functions, or shared helper modules with concrete code diffs. Warns against premature abstraction (single-use helpers).

When to use it

  • Test suite has grown to 20+ files with copy-paste duplication.
  • Onboarding pain because new tests are written by copying existing ones.
  • Adding shared concerns (logging, instrumentation) consistently across tests.
  • Refactoring before a major framework upgrade — DRY first, then upgrade is easier.

The prompt

XML-tagged — best for Claude 4.x

<role>
You are a test infrastructure engineer. You know that DRY in tests is a balance — too much abstraction hides intent; too little creates rot. You propose refactors that pay back in maintenance, not premature ones.
</role>

<context>
Common DRY opportunities in tests:
- **beforeEach duplication** — same setup steps in multiple files → fixture
- **Helper duplication** — same helper function defined inline in multiple files → shared module
- **Assertion pattern duplication** — same group of assertions on the same object shape → custom matcher / assertion helper
- **Test data duplication** — same fixtures (users, products) created inline → factory function
- **POM duplication** — same UI flow scripted in multiple specs → POM method

Anti-pattern: extracting a helper used ONLY ONCE. Inline > abstraction when called from one place.
</context>

<task>
For the test files I provide:
1. Identify EXACT duplication (3+ occurrences of same logic).
2. Identify NEAR duplication (2+ occurrences with minor variation).
3. Propose refactor per duplication:
   - For setup → Playwright fixture (or Jest `beforeEach` helper)
   - For data → factory function
   - For assertion → custom matcher or assertion helper
   - For UI flow → POM method
4. Diff-style output showing the new shared module + per-file diff.
5. Warn against premature abstraction — single-use helpers should be inlined.
</task>

<input>
Test files (paths + content): {test_files}
Framework: {framework}
Existing shared modules (if any): {existing_shared}
</input>

<constraints>
- Threshold: 3+ exact duplications, OR 2+ near-duplications with > 80% similarity.
- Single-use helpers FLAG as anti-pattern, not as refactor candidate.
- Diffs MUST be diff-style (—/+) showing exact lines added/removed.
- Suggested abstraction names should be domain-specific (not `utilA`, `helperX`).
- Note any shared concern that's organizational (e.g., logging) vs functional (test logic).
</constraints>

<output_format>
Three sections:
1. **Duplication inventory** — table: Pattern | Occurrences | Files | Recommended refactor
2. **Refactor diffs** — per refactor, show new shared file + per-file modification
3. **Warnings** — flag single-use helpers and other premature-abstraction risks
</output_format>

Before writing, count occurrences of each candidate pattern — single occurrences are NOT refactor candidates.

Example

Common pitfalls

  • Model proposes extracting single-use helpers — anti-pattern; force the 3+ occurrence rule.
  • Refactors lose context (helpers named `helper1`, `utilA`); insist on domain-named abstractions.
  • Diffs aren't really diffs (just rewritten files) — make sure — / + format is used.
  • Over-abstracts data (factory for a 2-line user object) — sometimes inline data is clearer.

Tips

  • Refactor in a separate PR — don't bundle DRY refactor with new test additions.
  • After refactor, run the whole suite to ensure behavior is unchanged.
  • Pair with `generate-playwright-pom` for UI flows that deserve POM extraction.
  • Pair with `pom-refactoring-reviewer` to verify the new abstractions don't violate POM principles.

FAQ

When test setup becomes a chain of abstractions that obscures what the test is actually exercising. If you have to navigate through 3 helper files to understand a single test, the abstractions are too deep. Inline beats abstraction when an abstraction has only 2-3 callers.

Related prompts