defect-first-testing

Use any time the user wants tests written for their code — 'write tests', 'add test coverage', 'test this function', 'test this module'. This is the primary test-authoring skill. Drives a defect-first workflow: analyze the production code for fault-prone patterns first, then write tests that target specific failure modes rather than just exercising the API. Produces tests that catch real bugs — not tests that compile and pass but detect nothing.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "defect-first-testing" with this command: npx skills add apankov1/quality-engineering/apankov1-quality-engineering-defect-first-testing

Defect-First Testing

Agents write tests that exercise APIs but catch zero bugs. They start from "what does this function do?" and produce tests that mirror the implementation. This skill reverses the workflow — start from "what bugs could exist in this code?" and write tests that would detect those bugs.

When to use: Writing tests for any function or module. Generating test files. Adding test coverage. Reviewing whether existing tests actually catch bugs.

When not to use: Writing implementation code. Measuring coverage metrics. Testing trivial getters/setters with no logic.

Rationalizations (Do Not Skip)

RationalizationWhy It's WrongRequired Action
"I'll test the happy path first"Happy-path tests catch zero bugs — the happy path already worksStart from fault surface, test defect scenarios first
"100% coverage means thorough testing"Coverage counts lines executed, not bugs caughtCheck that each test targets a specific defect class
"The function signature tells me what to test"Signatures describe contracts, not failure modesAnalyze the implementation for fault-prone patterns
"I'll add edge cases later""Later" never comes — and agents don't revisitIdentify edge cases up front via fault surface analysis

What To Protect (Start Here)

Before writing tests, analyze the production code for fault-prone patterns:

DecisionQuestion to AnswerIf Yes → Check Fault Class
Boundaries are correctDoes the code compare values or use indices?off-by-one, boundary-zero, empty-collection
Null/undefined is handledDoes the code access properties that could be null?null-undefined
Errors propagate correctlyDoes the code catch or throw errors?missing-error-path, swallowed-error, wrong-error-type
Types are validatedDoes the code convert or coerce types?type-coercion, nan-propagation
Math is safeDoes the code divide, modulo, or use domain-restricted functions?division-by-zero, nan-propagation
Mutations are intentionalDoes the code modify arrays/objects in place?shared-mutation
Async failures surfaceDoes the code use Promise.all or .catch?unhandled-rejection
All branches executeDoes the code have switch/if-else chains?missing-branch

The Defect-First Workflow

Step 1: Analyze the Fault Surface

Read the production code and call analyzeFaultSurface(source). This scans for patterns that historically produce bugs and returns a structured fault surface.

const surface = analyzeFaultSurface(productionCode);
// surface.entries — each fault with line, defect class, and test strategy
// surface.summary — fault counts per category
// surface.coverage — unique defect classes found

Step 2: Generate Test Suggestions

Call suggestTests(surface) to get concrete test case suggestions for each defect class.

const suggestions = suggestTests(surface);
// Each suggestion: name, defectComment, inputs, expectedBehavior

Step 3: Write Tests

For each suggestion, write a test that:

  1. Starts with a // Defect: comment explaining what production bug this catches
  2. Constructs inputs that trigger the specific fault
  3. Asserts on the specific behavior that would break if the defect existed
// Defect: off-by-one in loop termination — iterating arr.length
//         instead of arr.length-1 causes reading past the last valid element
it('handles boundary at last element', () => {
  const result = processItems([1, 2, 3]);
  assert.equal(result.lastProcessed, 3);
});

Step 4: Validate Coverage

Call validateCoverage(testSource, surface) to check that your tests cover the identified fault surface.

const validation = validateCoverage(testSource, surface);
// validation.covered — number of defect classes with targeting tests
// validation.gaps — defect classes with no targeting test
// validation.score — 0-100 coverage score

Included Utilities

import {
  analyzeFaultSurface,
  suggestTests,
  validateCoverage,
  formatTestPlan,
} from './defect-first.ts';

Key Principle: Every Test Needs a Defect Hypothesis

A test without a defect hypothesis is just an API exercise. Before writing it('should return X'), answer: "What production bug does this test catch?"

If you can't name the bug, don't write the test.

Bad (API exercise)Good (defect hypothesis)
it('returns an array')it('returns empty array for empty input, not undefined')
it('handles valid input')it('rejects NaN when numeric input expected')
it('calls the callback')it('calls callback exactly once, not per retry attempt')

Violation Rules

RuleSeverityDescription
Tests written without fault surface analysismust-fixEvery test file must be preceded by analyzeFaultSurface()
Test without // Defect: commentshould-fixEvery it() block should name its defect hypothesis
Defect class in surface with no targeting testshould-fixvalidateCoverage() reports gaps
Test asserts on type/truthiness onlymust-fixAssertions must verify specific values, not just existence

Companion Skills

  • slop-test-detector: Run after generating tests to catch remaining slop patterns. If analyzeTestFile() reports must-fail findings, the tests need rework.
  • fault-injection-testing: For testing failure paths in code with external dependencies.
  • pairwise-test-coverage: For combinatorial input coverage when multiple parameters interact.
  • model-based-testing: For testing state machine transitions.

Reference

See references/fault-catalog.md for the complete catalog of 16 code patterns and their associated defect classes, with before/after examples.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

websocket-client-resilience

No summary provided by upstream source.

Repository SourceNeeds Review
General

pairwise-test-coverage

No summary provided by upstream source.

Repository SourceNeeds Review
General

breaking-change-detector

No summary provided by upstream source.

Repository SourceNeeds Review