Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Generate characterization tests to capture and verify existing behavior before migration
.claude/skills/a5c-ai-characterization-test-generator/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 227% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 257% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 116% | 0% |
| case-04 | ✗→✓ | ▲ Improved | -5% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 132% | 0% |
Generates characterization tests (also known as golden master tests or approval tests) to capture existing system behavior before migration, ensuring functional equivalence after changes.
Enable behavior preservation during migration through:
This skill can leverage the following external tools when available:
| Tool | Purpose | Integration Method | |------|---------|-------------------| | ApprovalTests | Approval testing framework | Library | | Jest snapshots | JavaScript snapshot testing | Framework | | pytest-snapshot | Python snapshot testing | Plugin | | TextTest | Golden master testing | CLI | | Verify | .NET approval testing | Library | | Scientist | Safe refactoring library | Library | | AI Testing MCP | AI-powered test generation | MCP Server |
bash# Invoke skill for characterization test generation # The skill will analyze code and generate tests # Expected inputs: # - targetPath: Path to code to characterize # - testFramework: 'jest' | 'pytest' | 'junit' | 'nunit' | 'auto' # - outputDir: Directory for generated tests # - captureMode: 'snapshot' | 'approval' | 'recording'
json{ "generationId": "string", "timestamp": "ISO8601", "target": { "path": "string", "language": "string", "framework": "string", "unitsAnalyzed": "number" }, "testsGenerated": { "total": "number", "byType": { "snapshot": "number", "approval": "number", "recording": "number" }, "coverage": { "functions": "number", "branches": "number", "lines": "number" } }, "characterizations": [ { "unit": "string", "type": "function|method|endpoint|class", "inputs": [ { "name": "string", "type": "string", "values": ["any"], "source": "existing|generated|boundary" } ], "outputs": { "type": "string", "goldenMasterPath": "string", "checksum": "string" }, "testFile": "string", "baselineApproved": "boolean" } ], "edgeCases": [ { "unit": "string", "case": "string", "input": "any", "expectedBehavior": "string", "covered": "boolean" } ], "dependencies": { "external": ["string"], "mocked": ["string"], "recorded": ["string"] }, "artifacts": { "testSuite": "string", "goldenMasters": "string", "recordings": "string", "coverageReport": "string" } }
This skill integrates with the following Code Migration/Modernization processes:
Create .characterization-tests.json in the project root:
json{ "testFramework": "auto", "outputDir": "./tests/characterization", "captureMode": "snapshot", "goldenMasterDir": "./tests/golden-masters", "inputGeneration": { "boundary": true, "combinatorial": true, "maxCombinations": 100, "includeNulls": true, "includeEmpty": true }, "outputCapture": { "format": "json", "normalizeWhitespace": true, "ignorePaths": ["$.timestamp", "$.requestId"], "tolerance": { "numeric": 0.001, "dateTime": "1s" } }, "dependencies": { "mockExternal": true, "recordMode": false, "replayMode": true }, "approval": { "requireApproval": true, "approvalDir": "./tests/approvals", "reportFormat": "markdown" }, "ci": { "failOnNewTests": false, "updateGoldenOnPass": false, "generateCoverageReport": true } }
When AI Testing MCP Server is available:
javascript// Example AI-powered test generation { "tool": "ai_testing_generate", "arguments": { "target": "./src/services/user.ts", "framework": "jest", "style": "characterization" } }
javascript// Generated characterization test describe('UserService', () => { describe('calculateDiscount', () => { it('should match snapshot for standard customer', () => { const result = userService.calculateDiscount({ customerId: 'C001', purchaseAmount: 100, loyaltyPoints: 500 }); expect(result).toMatchSnapshot(); }); it('should match snapshot for premium customer', () => { const result = userService.calculateDiscount({ customerId: 'C002', purchaseAmount: 100, loyaltyPoints: 5000, isPremium: true }); expect(result).toMatchSnapshot(); }); // Edge cases it('should match snapshot for zero amount', () => { const result = userService.calculateDiscount({ customerId: 'C001', purchaseAmount: 0, loyaltyPoints: 0 }); expect(result).toMatchSnapshot(); }); }); });
java// Generated approval test public class UserServiceCharacterizationTest { @Test public void calculateDiscount_standardCustomer() { UserService service = new UserService(); DiscountResult result = service.calculateDiscount( new DiscountRequest("C001", 100.0, 500) ); Approvals.verify(result); } @Test public void calculateDiscount_boundaryValues() { UserService service = new UserService(); // Boundary: minimum values Approvals.verify(service.calculateDiscount( new DiscountRequest("C001", 0.01, 0) ), "minimum"); // Boundary: maximum values Approvals.verify(service.calculateDiscount( new DiscountRequest("C001", 999999.99, 999999) ), "maximum"); } }
python# Generated recording-based test import pytest from tests.recordings import PlaybackRecorder class TestUserServiceCharacterization: @pytest.fixture def recorder(self): return PlaybackRecorder('tests/recordings/user_service') def test_get_user_profile_recorded(self, recorder): """Replay recorded external API interactions""" with recorder.playback('get_user_profile_c001'): service = UserService() result = service.get_user_profile('C001') assert result == recorder.expected_output() def test_update_user_settings_recorded(self, recorder): """Verify database interactions match recording""" with recorder.playback('update_settings_c001'): service = UserService() result = service.update_settings('C001', {'theme': 'dark'}) recorder.verify_database_calls() assert result == recorder.expected_output()
test-coverage-analyzer: Analyze coverage gapsmigration-validator: Validate migration resultsstatic-code-analyzer: Identify testable code pathsmigration-testing-strategist: Uses this skill for test strategyregression-detector: Uses this skill for regression detectionparallel-run-validator: Uses this skill for comparison testing| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 10,203 | 22,216 | +118% | 1 | 1 | 0% | 2,424 | 7,924 | +227% | 0 | 0 | — |
case-02 | fail→pass | 8,796 | 17,023 | +94% | 1 | 1 | 0% | 1,882 | 6,722 | +257% | 0 | 0 | — |
case-03 | fail→pass | 12,630 | 14,785 | +17% | 1 | 1 | 0% | 2,855 | 6,180 | +116% | 0 | 0 | — |
case-04 | fail→pass | 40,332 | 2,321 | -94% | 1 | 1 | 0% | 3,371 | 3,195 | -5% | 0 | 0 | — |
case-05 | fail→fail | 12,686 | 15,823 | +25% | 1 | 1 | 0% | 2,563 | 6,004 | +134% | 0 | 0 | — |
case-06 | fail→pass | 10,242 | 6,108 | -40% | 1 | 1 | 0% | 1,593 | 3,698 | +132% | 0 | 0 | — |
case-07 | fail→pass | 17,390 | 16,579 | -5% | 1 | 1 | 0% | 4,025 | 6,298 | +56% | 0 | 0 | — |
case-08 | fail→fail | 9,105 | 8,559 | -6% | 1 | 1 | 0% | 1,828 | 4,503 | +146% | 0 | 0 | — |
case-09 | fail→fail | 19,611 | 1,872 | -90% | 1 | 1 | 0% | 3,633 | 3,161 | -13% | 0 | 0 | — |
case-10 | pass→pass | 9,477 | 4,063 | -57% | 1 | 1 | 0% | 1,849 | 3,557 | +92% | 0 | 0 | — |
case-11 | fail→pass | 10,782 | 1,592 | -85% | 1 | 1 | 0% | 2,178 | 3,065 | +41% | 0 | 0 | — |
case-12 | fail→fail | 10,506 | 9,249 | -12% | 1 | 1 | 0% | 1,816 | 4,160 | +129% | 0 | 0 | — |
case-13 | fail→pass | 10,482 | 3,250 | -69% | 1 | 1 | 0% | 1,738 | 3,261 | +88% | 0 | 0 | — |
case-14 | fail→pass | 12,927 | 1,817 | -86% | 1 | 1 | 0% | 1,754 | 3,027 | +73% | 0 | 0 | — |
case-15 | fail→pass | 13,345 | 10,932 | -18% | 1 | 1 | 0% | 2,106 | 4,269 | +103% | 0 | 0 | — |
case-16 | pass→pass | 16,641 | 17,974 | +8% | 1 | 1 | 0% | 2,547 | 5,607 | +120% | 0 | 0 | — |
case-17 | fail→fail | 10,092 | 3,152 | -69% | 1 | 1 | 0% | 1,702 | 3,354 | +97% | 0 | 0 | — |
case-18 | fail→fail | 14,353 | 13,686 | -5% | 1 | 1 | 0% | 2,234 | 5,179 | +132% | 0 | 0 | — |
case-19 | fail→fail | 12,940 | 16,686 | +29% | 1 | 1 | 0% | 2,055 | 5,462 | +166% | 0 | 0 | — |
case-20 | fail→fail | 17,397 | 16,516 | -5% | 1 | 1 | 0% | 2,820 | 6,276 | +123% | 0 | 0 | — |
case-21 | fail→fail | 17,958 | 17,423 | -3% | 1 | 1 | 0% | 3,474 | 5,959 | +72% | 0 | 0 | — |
case-22 | fail→fail | 19,260 | 12,352 | -36% | 1 | 1 | 0% | 3,208 | 5,113 | +59% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +45 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.