characterisation-tests

Write tests that document the actual current behavior of untested legacy code before refactoring.

Updated May 18, 2024
One-click install
npx skills add https://github.com/joshhornby/dotfiles --skill characterisation-tests-joshhornby
Or copy as Structured Prompt for Agent
Please help me install this Agent Skill.
Skill: characterisation-tests
Source: https://github.com/joshhornby/dotfiles/tree/main/.claude/skills/characterisation-tests
Command: npx skills add https://github.com/joshhornby/dotfiles --skill characterisation-tests-joshhornby

SYSTEM DOCUMENTATION & REQUIREMENTS

What problem does it solve? You need to modify legacy code that has no tests, but refactoring without a safety net risks silently changing behavior. This Skill provides a disciplined method for pinning down what the code actually does today — not what it should do — so you can refactor with confidence and detect any unintended behavioral change. ## Core Features & Use Cases - Characterisation Algorithm: A five-step loop (use the code, write a failing dummy assertion, let the failure reveal actual behavior, record it, repeat) that turns the code itself into the specification. - Naming and Lifecycle Conventions: Enforces characterises test names, .characterisation.test.ts file suffixes, and header comments so these temporary scaffolding tests are never confused with permanent behavior-driven tests. - Async and Non-Determinism Handling: Covers characterising async functions, error paths, event emitters, timestamps, and random values using seams, fake timers, and Vitest snapshots. - Use Case: You must change a bonus-calculation function with zero tests and no documentation. Apply the algorithm to pin down its current outputs across tiers, boundaries, and edge cases, mark suspicious behavior explicitly, then refactor safely and replace the scaffolding with proper tests. ## Quick Start Ask the AI to write characterisation tests for the untested legacy function you are about to modify, documenting its actual current behavior before any refactoring.

Frequently Asked Questions about characterisation-tests

High-intent search queries and answers about installing and using this skill.

FAQPage Schema
How do I write tests for legacy code that has no tests?

Use the characterisation algorithm: call the code in a test harness, write an assertion you know will fail with a placeholder value, let the test failure reveal the actual behavior, then update the test to expect that behavior. Repeat, letting the code guide what to test next.

What is the difference between characterisation tests and unit tests?

Characterisation tests document what code actually does, without asserting correctness, and are temporary scaffolding for safe refactoring. Behavior-driven unit tests assert intended behavior and are permanent. Replace characterisation tests with proper tests as you understand the code.

How do I test async legacy code with Vitest?

Use the same fail-first algorithm but await results, and characterise both resolved and rejected states with rejects.toThrow. Introduce a seam by passing async dependencies as function parameters, and use vi.useFakeTimers to control timing-dependent behavior.

When should I stop writing characterisation tests?

Stop when every branch your upcoming change touches has a test, plus one layer out to callers and callees, and likely mutation risks in those paths are covered. You do not need full codebase coverage — only confidence around the change area.

What should I do when characterisation reveals a bug?

Document the actual behavior in the test and mark it explicitly as suspicious rather than silently fixing it. If the system is deployed, preserve the behavior and escalate before changing it, since consumers may depend on it.

When should I not use characterisation tests?

Do not use them for greenfield code, which should be test-driven from the start, or when clear specifications already exist for new code. Also avoid them as a permanent testing strategy — they are scaffolding to be replaced after refactoring.