test-discipline
Skill50 starsChanged 16 days agoMITView on GitHub
Testing practices for behavior-focused, refactor-resistant tests. Use when implementing features, fixing bugs, refactoring, or adding or reviewing tests to ensure they verify intent through public interfaces and avoid tautological assertions. Also use when adding regression tests after bug fixes. Consulted by the implement agent during coding and by the review agent when validating test quality. Do not use for test strategy decisions (use complexity-analysis), for failure triage and root-cause analysis (use debugging-recovery, then after root cause is localized use test-discipline to add the reproducing regression test and fix), or for integration test infrastructure setup.
We split this skill into the rules it gives an agent
10 rules. 0 are common, 10 are distinctive, and 0 are very specific to this project.
0
Common
Most of the 11 edited versions we read keep these.
10
Distinctive
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 12
Use TDD strategically, not dogmatically.
- Distinctive: line 16
Test-first (red-green-refactor) works best when:
- Distinctive: line 18
Fixing a bug: Write a test that reproduces the bug before fixing it.
- Distinctive: line 28
Well-understood patterns: When applying a known pattern (CRUD, adapter, pipeline), write the implementation, then test the behavior.
- Distinctive: line 58
Verify: Run the full test suite to confirm relevant existing tests and any justified new tests pass without regressions
- Distinctive: line 64
Every test must satisfy these criteria regardless of when it was written:
- Distinctive: line 84
Test private methods or internal state
- Distinctive: line 105
Use the independently established payable amount:
- Distinctive: line 117
Run the existing behavioral tests before changing code to establish a baseline, then run them after the refactor.
- Distinctive: line 119
Do not add tests solely for extracted private helpers, delegation details, renamed classes, or structural equivalence between old and new …
“Common” is measured: kept by at least 6 in 10 of the 11 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Test Discipline
Philosophy
Tests verify behavior through public interfaces, not implementation details. Code can change entirely; tests should not break unless behavior changes. A good test reads like a specification.
Use TDD strategically, not dogmatically. The goal is high-quality tests that verify intent — the ordering of when tests are written is a means to that end, not the end itself.
When to Write Tests First
Test-first (red-green-refactor) works best when:
- Fixing a bug: Write a test that reproduces the bug before fixing it. The test must fail BEFORE the fix and pass AFTER. If the bug is not reproducible via automated test (race condition, environment-specific), document why and what alternative evidence proves the fix.
- Behavior is unclear: The test forces you to define expected behavior precisely before writing code.
- Working on a well-defined interface: The contract is clear and the test is straightforward to write.
When to Write Tests After (or Interleave)
Design-first-then-test works best when:
- Exploring a new domain: The design is not yet clear. Sketch interfaces and responsibilities first, write code in meaningful blocks, then add tests.
- Prototyping: Rapid iteration where the interface is still evolving. Tests written too early against an unstable interface create waste.
- Well-understood patterns: When applying a known pattern (CRUD, adapter, pipeline), write the implementation, then test the behavior.
In both approaches, the quality criteria for the resulting tests are identical.
Vertical Slices
Regardless of test-first or design-first, deliver work in vertical slices — thin end-to-end paths through all layers.
Anti-pattern: horizontal slicing
Writing all tests first (or all of one layer first), then all implementation. This produces tests that verify imagined behavior and delays integration feedback.
AVOID (horizontal):
All tests → All implementation
All models → All services → All endpoints
PREFER (vertical):
Slice 1: User can create an order (all layers, happy path)
Slice 2: Order validates required fields (error handling)
Slice 3: Order calculates totals (business logic)The first slice should be a tracer bullet: a thin end-to-end path that proves the architecture works.
Per-Task Procedure
- Choose the next vertical slice (tracer bullet first)
- Design: Sketch the interface and responsibilities for this slice
- Implement + Test: Either test-first (RED → GREEN) or implement-then-test, based on the context guidance above
- Verify: Run the full test suite to confirm relevant existing tests and any justified new tests pass without regressions
- Refactor: Clean up while all tests are green. Never refactor with failing tests.
- Repeat: Next slice
Test Quality Criteria
Every test must satisfy these criteria regardless of when it was written:
| Criterion | Check |
| Behavior-focused | Tests what the system does, not how it does it |
| Public interface only | Uses the same API that callers use |
| Refactor-resistant | Would survive internal restructuring without changes |
| Independent oracle | Expected outcomes follow a requirement, concrete example, or independently justified property rather than repeating the implementation |
| Independent | Does not depend on other tests or test execution order |
| Deterministic | Same result every run, no timing dependencies |
| Readable | Test name describes the behavior being verified |
For unit test rules, integration test rules, and test doubles hierarchy, follow coding-guidelines.md (always in context). This skill focuses on what coding-guidelines does not cover: the decision framework, per-task procedure, quality criteria, and anti-patterns.
What Makes a Good Test
Good tests exercise real code paths through public APIs. They describe what the system does, not how. They read like a specification: "user can checkout with valid cart" tells you exactly what capability exists.
Bad tests are coupled to implementation:
- Mock internal collaborators
- Test private methods or internal state
- Bypass the public interface of the component under test with side-channel assertions (unless persistence itself is the behavior being verified)
- Break when you refactor, even though behavior has not changed
Warning sign: a test breaks when you refactor, but behavior has not changed. That test was testing implementation, not behavior.
Avoid Tautological Tests
A tautological test derives its expectation from the implementation it is meant to verify, so the same mistake can appear on both sides of the assertion. Passing results then provide false confidence.
- Establish the oracle: Derive expected outcomes from requirements, concrete examples, externally visible contracts, or independently justified properties. Do not use the result under test to generate its own expectation or copy the production algorithm into the assertion.
- Name a regression the assertion would catch: Identify a plausible contract violation and check that the assertion would fail for it. This is a reasoning check and does not require mutation-testing tooling.
- Keep helpers independent of the behavior under test: Shared fixture builders are fine. Avoid using the same formatting or calculation helper on both sides when correctness of that operation is part of the asserted contract.
- Assess what the assertion proves: Computed expectations, property-based tests, independent reference implementations, snapshots, and mocks can be valid. A round-trip property alone can miss matching encoder and decoder bugs, and an unreviewed snapshot can preserve a defect. Asserting a stubbed value is meaningful when forwarding that value is the public contract, but does not verify the stubbed dependency's calculation.
For a contract where a 20 percent discount on 1,000 cents leaves 800 cents payable, avoid copying a buggy production calculation that returns the discount amount instead:
assert discounted_total(1000, 20) == 1000 * 20 // 100Use the independently established payable amount:
assert discounted_total(1000, 20) == 800The second assertion catches returning the discount amount. Additional cases should address relevant contract gaps, such as rounding, rather than repeat the same formula across many inputs.
Reference: Tautological Tests by Randy Coulman.
Behavior-Preserving Refactoring
- Run the existing behavioral tests before changing code to establish a baseline, then run them after the refactor.
- Reuse existing tests when they already protect the affected contract. Add tests only for a concrete coverage gap in behavior that must remain stable, preferably before restructuring that code.
- Do not add tests solely for extracted private helpers, delegation details, renamed classes, or structural equivalence between old and new implementations. Comparing two implementations can support a refactor but does not independently establish that either meets the requirements.
- Preserve behavioral assertions when adapting setup or call sites to an intentional interface change. Do not rewrite expected outcomes merely to match the refactored output.
Integration with SDD Lifecycle
| Agent | How test discipline is used |
devsquad.implement.execute | Follows per-task procedure for each task |
devsquad.review.tests | Uses test quality criteria as review rubric |
quality-gate (code rubric) | Verifies behavior coverage and independent oracles, including existing tests for refactors |
debugging-recovery | Adds regression test (test that reproduces bug, then fix) |
Anti-patterns
| Anti-pattern | Problem | Fix |
| All tests first, then all code | Tests verify imagined behavior | Work in vertical slices |
| Mocking what you own | Tests break on refactor | Test through public interface with real implementations |
| Testing private methods | Coupled to implementation | Test the behavior those methods enable |
| Tautological assertions | The same bug can appear in implementation and expectation | Derive the expectation from an independent behavioral oracle |
| New tests for every refactor | Tests mirror new structure without protecting additional behavior | Reuse behavioral tests and add only concrete coverage gaps |
| 100% coverage as a goal | Coverage without behavior focus is noise | Cover critical paths and edge cases |
| Copy-paste test setup | Fragile, hard to maintain | Extract test helpers and fixtures |
| Testing the framework | Verifying framework behavior, not yours | Trust the framework, test your logic |
| Tests without assertions | False confidence — tests pass but verify nothing | Every test must assert observable behavior |
| Dogmatic test-first in unfamiliar domains | Premature tests lock in unstable interfaces | Design first, then test when the interface stabilizes |
Checklist Per Slice
[ ] Test describes behavior, not implementation
[ ] Test uses public interface only
[ ] Test would survive internal refactor
[ ] Expected outcome has an independent behavioral oracle
[ ] Assertion would catch a plausible contract violation
[ ] New tests address concrete behavior gaps rather than refactoring structure
[ ] Test doubles follow coding-guidelines hierarchy
[ ] No speculative features added
[ ] Refactoring done only while all tests pass
[ ] Suite runs green before moving to next sliceOther versions13 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 13
- github/gh-aw★ 5.5ksame name
- microsoft/Generative-AI-for-beginners-dotnet★ 3.2ksame name
- microsoft/waza★ 1.5ksame name
- Azure/fleet★ 227same name
- kubefleet-dev/kubefleet★ 162same name
- Dimon94/dverity★ 107same name
- Dimon94/skills★ 107same name
- Azure/apiops-cli★ 32same name
- microsoft/github-vteam-industry-demos★ 2same name
- niksavis/basicly★ 2same name
3 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
Used alongsideProjects with it also use squad-commands, architectural-proposals and 10 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
MicrosoftSkillsquad-commands
Categorized catalog of common Squad operations. Coordinator reads this file and presents it as an interactive menu when the user asks for available commands or help.
Also in 4 projects that use this
MicrosoftSkillarchitectural-proposals
How to write comprehensive architectural proposals that drive alignment before code is written
Also in 3 projects that use this
- +10 more from microsoft
In this repository26 other files it gives its agents.
- .github/copilot-instructions.mdCopilot instructions
- .github/plugins/devsquad/hooks/templates/.github/copilot-instructions.mdCopilot instructions
- adr-workflowSkill
- board-configSkill
- complexity-analysisSkill
- debugging-recoverySkill
- deep-clarificationSkill
- diagram-designSkill
- documentation-styleSkill
- domain-glossarySkill
- engineering-practicesSkill
- git-branchSkill
- git-commitSkill
- harness-learningsSkill
- init-configSkill
- init-docsSkill
- init-scaffoldSkill
- lsp-setupSkill
- next-taskSkill
- pull-requestSkill
- quality-gateSkill
- reasoningSkill
- security-reviewSkill
- triage-workflowSkill
- work-item-creationSkill
- work-item-workflowSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

