declutter-test-harness
Test harness creation skill - creates characterization tests to preserve behavior during refactoring
What this skill does
# Test Harness Creation
## The Iron Law
```
╔═══════════════════════════════════════════════════════════════════╗
║ NO REFACTORING WITHOUT CHARACTERIZATION TESTS FIRST ║
╚═══════════════════════════════════════════════════════════════════╝
```
## Purpose
Create a safety net of tests that capture the **current behavior** of code before refactoring. These tests ensure that refactoring preserves behavior, even if that behavior has bugs.
## Key Principle
> "Characterization tests document what the system actually does, not what it should do."
> — Michael Feathers, Working Effectively with Legacy Code
## Test Harness Workflow
```dot
digraph test_harness {
rankdir=TB;
node [shape=box];
identify [label="1. Identify\nRefactoring Target"];
entry [label="2. Find\nEntry Points"];
capture [label="3. Write Tests\nCapturing Behavior"];
run [label="4. Run Tests"];
pass [label="All Pass?" shape=diamond];
done [label="Harness Complete" shape=ellipse];
debug [label="Debug Test\n(Not Code!)"];
identify -> entry;
entry -> capture;
capture -> run;
run -> pass;
pass -> done [label="YES"];
pass -> debug [label="NO"];
debug -> capture;
}
```
## Step 1: Identify Entry Points
For the code being refactored, identify all ways it can be called:
```markdown
- [ ] Public methods/functions
- [ ] Event handlers
- [ ] API endpoints
- [ ] CLI commands
- [ ] Scheduled jobs
- [ ] Message queue consumers
```
## Step 2: Capture Input/Output Pairs
For each entry point, document:
| Input | Expected Output | Side Effects |
|-------|-----------------|--------------|
| Typical case | Current result | DB writes, logs |
| Edge case 1 | Current result | ... |
| Error case | Current exception | ... |
| Null/empty | Current behavior | ... |
## Step 3: Write Characterization Tests
### Python Template
```python
import pytest
from module_under_test import target_function
class TestTargetFunctionCharacterization:
"""
Characterization tests for target_function.
These capture CURRENT behavior, not desired behavior.
DO NOT modify expected values during refactoring.
"""
def test_typical_input_returns_expected_output(self):
# Arrange
input_data = {"user_id": 123, "action": "purchase"}
# Act
result = target_function(input_data)
# Assert - This is what it DOES, not what it SHOULD do
assert result == {"status": "ok", "transaction_id": "expected_id"}
def test_empty_input_returns_error(self):
# Arrange
input_data = {}
# Act
result = target_function(input_data)
# Assert
assert result == {"status": "error", "message": "Missing user_id"}
def test_invalid_action_behavior(self):
# Arrange
input_data = {"user_id": 123, "action": "invalid"}
# Act & Assert - Document current exception behavior
with pytest.raises(ValueError) as exc:
target_function(input_data)
assert "Unknown action" in str(exc.value)
def test_side_effects_are_captured(self, mock_database):
# Arrange
input_data = {"user_id": 123, "action": "purchase"}
# Act
target_function(input_data)
# Assert side effects
mock_database.insert.assert_called_once_with(
"transactions",
{"user_id": 123, "action": "purchase"}
)
```
### TypeScript Template
```typescript
import { targetFunction } from './module-under-test';
describe('targetFunction characterization', () => {
/**
* Characterization tests - capture CURRENT behavior.
* DO NOT modify expected values during refactoring.
*/
it('handles typical input correctly', () => {
const input = { userId: 123, action: 'purchase' };
const result = targetFunction(input);
expect(result).toEqual({ status: 'ok', transactionId: 'expected_id' });
});
it('handles empty input', () => {
const input = {};
const result = targetFunction(input);
expect(result).toEqual({ status: 'error', message: 'Missing userId' });
});
it('throws on invalid action', () => {
const input = { userId: 123, action: 'invalid' };
expect(() => targetFunction(input)).toThrow('Unknown action');
});
it('logs expected messages', () => {
const consoleSpy = jest.spyOn(console, 'log');
const input = { userId: 123, action: 'purchase' };
targetFunction(input);
expect(consoleSpy).toHaveBeenCalledWith('Processing purchase for 123');
});
});
```
## Step 4: Handle Dependencies
### Mocking Strategy
| Dependency Type | Strategy |
|-----------------|----------|
| Database | Mock at boundary |
| External API | Mock responses |
| File system | Use temp directory |
| Time/dates | Freeze time |
| Random | Seed or mock |
### Dependency Injection for Testability
If code is too coupled to dependencies:
1. Extract dependency to parameter
2. Provide default for production
3. Inject mock for tests
```python
# Before: Hard to test
def process_order(order_id):
db = DatabaseConnection() # Hidden dependency
order = db.get_order(order_id)
# ...
# After: Testable
def process_order(order_id, db=None):
db = db or DatabaseConnection() # Injectable
order = db.get_order(order_id)
# ...
```
## Step 5: Verify Harness
Before proceeding to refactoring:
```markdown
- [ ] All characterization tests pass
- [ ] Each entry point has at least one test
- [ ] Edge cases are covered
- [ ] Error paths are covered
- [ ] Side effects are verified
- [ ] Tests fail when behavior changes
```
### Mutation Testing (Optional but Recommended)
Verify tests actually catch changes:
```bash
# Python
mutmut run --paths-to-mutate=module_under_test.py
# JavaScript
npx stryker run
```
## Red Flags - STOP
| Thought | Reality |
|---------|---------|
| "The existing tests are enough" | Verify it. Run them. Check coverage. |
| "I'll add tests after refactoring" | You'll miss behavior changes. |
| "This code is too tangled to test" | That's why you need the harness. Extract seams first. |
| "Testing this is too slow" | Slow tests > broken refactoring |
## Checklist
```markdown
- [ ] Entry points identified
- [ ] Input/output pairs documented
- [ ] Characterization tests written
- [ ] All tests passing
- [ ] Edge cases covered
- [ ] Error paths covered
- [ ] Side effects verified
- [ ] Dependencies properly mocked
```
## Signal
When harness is complete, emit:
```
HARNESS_COMPLETE
```
Related in Code Review
gstack
IncludedFast headless browser for QA testing and site dogfooding. Navigate pages, interact with elements, verify state, diff before/after, take annotated screenshots, test responsive layouts, forms, uploads, dialogs, and capture bug evidence. Use when asked to open or test a site, verify a deployment, dogfood a user flow, or file a bug with screenshots. (gstack)
startup-due-diligence
IncludedLegal due diligence review for seed-stage and Series A startups (US, Delaware C-Corp focus). Supports both investor and founder perspectives. Capabilities include: (1) Interactive document review and issue spotting; (2) Document request list generation; (3) Cap table and SAFE/convertible note analysis; (4) Red flag identification with severity ratings; (5) Diligence report generation. TRIGGERS: due diligence, DD, startup investment, cap table review, Series A, seed round, investor diligence, legal review startup, SAFE analysis, convertible note, 409A, founder vesting.
interview-master
IncludedThis skill should be used when the user asks to "generate interview questions", "prepare for interview", "optimize resume", "conduct mock interview", "analyze git commits for resume", "generate resume from code", "review my resume", or mentions interview preparation, career assistance, or extracting project experience from git history. Provides comprehensive interview and career development guidance for both job seekers and interviewers.
fix-issue
IncludedFixes GitHub issues using parallel analysis agents for root cause investigation, code exploration, and regression detection. Reads issue context from gh CLI, searches codebase and memory for related patterns, generates a fix with tests, and links the resolution back to the issue via PR. Includes prevention analysis to avoid recurrence. Use when debugging errors, resolving regressions, fixing bugs, or triaging issues.
sf-apex
IncludedGenerates and reviews Salesforce Apex code with 150-point scoring. TRIGGER when: user writes, reviews, or fixes Apex classes, triggers, test classes, batch/queueable/schedulable jobs, or touches .cls/.trigger files. DO NOT TRIGGER when: LWC JavaScript (use sf-lwc), Flow XML (use sf-flow), SOQL-only queries (use sf-soql), or non-Salesforce code.
swift-development
IncludedComprehensive Swift development for building, testing, and deploying iOS/macOS applications. Use when Claude needs to: (1) Build Swift packages or Xcode projects from command line, (2) Run tests with XCTest or Swift Testing framework, (3) Manage iOS simulators with simctl, (4) Handle code signing, provisioning profiles, and app distribution, (5) Format or lint Swift code with SwiftFormat/SwiftLint, (6) Work with Swift Package Manager (SPM), (7) Implement Swift 6 concurrency patterns (async/await, actors, Sendable), (8) Create SwiftUI views with MVVM architecture, (9) Set up Core Data or SwiftData persistence, or any other Swift/iOS/macOS development tasks.