Choose the right test level for each behaviour and write tests that fail only for real reasons. Covers the microservice test portfolio — unit, component (service in isolation with real infrastructure and stubbed collaborators), integration, and narrow end-to-end — plus what each level must not own, test doubles discipline, determinism and flakiness, arrange-act-assert structure, assertion quality, coverage versus value, mutation testing, and test data management. Use when writing or reviewing any test, deciding what level to test at, diagnosing a flaky or slow suite, or when the user says 'tests', 'unit test', 'component test', 'test pyramid', 'mocking', 'flaky', or 'coverage'. This skill governs test design inside a service — cross-service contract verification belongs to contract-testing.
A test suite has one job: tell you, quickly and truthfully, whether a change is safe to ship. Most
suites fail at this in one of two ways — they are so slow and flaky that people stop believing
them, or they are so tightly bound to implementation that every refactor breaks a hundred tests
while real defects walk through.
Both come from testing at the wrong level. The level is the decision that matters; everything else
is craft.
Config Resolution
Read .msskills/config.yaml in the repo root; look up paths.testing.
A custom document exists at that path → read its frontmatter mode:
override → use it alone as the testing standard; ignore the defaults below.
overlay (default, or no mode key) → read defaults first, then
apply the custom document's sections on top, matched by exact heading; new sections append.
A path is configured but no file exists there → say which path is missing, then use the defaults.
No config file or no paths.testing key → use defaults.
.msskills/stack.md exists → use its test framework, assertion style, container/stub tooling,
and naming convention. Match the conventions of the tests already in the repository over these
defaults.
Choosing the level
Test each behaviour once, at the cheapest level that can genuinely prove it.
Level
Boundary
Real
Stubbed
Owns
Unit
A class or function
The code under test and its value objects
Everything with I/O
Business rules, calculations, state machines, edge cases
Component
One whole service
The service, its database, its HTTP and message entry points
A handful of critical user journeys, and nothing more
Component tests are the highest-value level in a microservice and the most often missing. One
service, real database in a container, real HTTP in and out, collaborators stubbed at the network
boundary: fast enough to run on every commit, and it proves the thing actually starts and works.
Self-Validation Checklist
STOP after writing or reviewing each test. Verify every check. Fix failures before presenting.
RIGHT LEVEL: Is this the cheapest level that can prove this behaviour? A rule covered by a
unit test does not also need an end-to-end test.
BEHAVIOUR NOT IMPLEMENTATION: Would this test survive a refactor that preserves behaviour?
If it asserts on internal calls, private state, or field order → rewrite against observable
outcomes.
ONE REASON TO FAIL: Does the test have a single clear subject? Multiple unrelated assertions
→ split, so a failure names the defect.
NAMED BY BEHAVIOUR: Does the name state the scenario and the expected outcome, so a failure
in CI is self-explanatory without opening the file?
AAA VISIBLE: Are arrange, act, and assert distinguishable? Assertions interleaved with
actions → restructure.
DETERMINISTIC: Does it depend on wall-clock time, real randomness, timezone, locale,
ordering, network, or another test's state? → inject the clock and the ID source; sort before
comparing; seed randomness.
ISOLATED: Does it pass when run alone, in a different order, and in parallel? Shared mutable
fixtures → give each test its own data.
NO SLEEP: Does it wait on a fixed sleep? → poll with a bounded timeout on the actual
condition.
DOUBLES JUSTIFIED: Is each test double replacing something slow, non-deterministic, or
external? Mocking a pure value object or the type under test → use the real thing.
ASSERTS THE POINT: Does the assertion check what the test claims to prove — including
values, not just that a call happened? verify(save called) does not prove the right thing was
saved.
FAILURE PATHS COVERED: Are timeouts, rejections, and compensation paths tested, not just
the happy path? Untested error handling is the code that runs during an incident.
FAILS FIRST: Would this test fail if the behaviour were removed? A test that passes against
a broken implementation proves nothing — check it.
All checks pass → state "Test holds: , , deterministic."
Active Anti-Pattern Scan
Any box you can check is a defect. Fix it before presenting.
Ice Cream Cone: mostly end-to-end tests, few unit tests → slow, flaky feedback; push
coverage down to the cheapest level that proves each behaviour.
Mockery: everything mocked including the subject's own collaborators, so the test asserts
the implementation back to itself → use real objects for anything without I/O.
Assertion-Free Test: exercises code and asserts nothing, or only that no exception was
thrown → assert the outcome.
Sleep-Based Waiting: sleep(2000) for async work → poll the condition with a timeout.
Shared Mutable Fixture: tests that pass only in a particular order, or fail in parallel →
per-test data with unique keys.
Snapshot Everything: large auto-generated snapshots accepted without reading → assert the
fields that matter.
Coverage Theatre: tests written to raise a percentage, exercising getters → delete them;
cover behaviour instead.
Test Logic: conditionals and loops in tests, so the test itself needs testing → use
parameterised cases.
Retried Flake: a failing test wrapped in a retry annotation → find the non-determinism;
a retried test is a test nobody trusts.
Mocked Third Party at the Wrong Layer: mocking the vendor SDK rather than stubbing its
HTTP → stub at the network boundary so serialisation is exercised.
Happy Path Only: no test for timeout, rejection, duplicate delivery, or compensation.
Slow Unit Suite: unit tests that start a container or a framework context → that is a
component test; move it.
Disabled Test: @Ignore/skip with no linked issue → fix or delete; a skipped test is
false comfort.
Ambiguity Signals
Route these through collaborative-judgment. Each has two defensible answers.
Unit or component. A rule reachable only through the HTTP layer can be unit tested by
extracting it, or component tested where it lives. Extraction is cleaner and is also a design
change.
How many end-to-end tests. They catch integration and environment faults nothing else does,
and they are the slowest and flakiest thing you own. The right number is small and non-zero.
Test doubles for another service. A stub is fast and can drift from reality; a real instance
in a container is faithful and slow. Contract tests are how you get both, at the cost of a broker
(see contract-testing).
Coverage targets. A number drives behaviour, and not always the behaviour you wanted. Mutation
score measures the same thing more honestly and costs more to run.
Fixtures versus builders. Shared fixtures are concise and couple tests together; per-test
builders are explicit and verbose.
Whether to test a thin adapter. Some layers are pure delegation, and a test for them asserts
the framework works.
1---2name: test-strategy3description: Choose the right test level for each behaviour and write tests that fail only for real reasons. Covers the microservice test portfolio — unit, component (service in isolation with real infrastructure and stubbed collaborators), integration, and narrow end-to-end — plus what each level must not own, test doubles discipline, determinism and flakiness, arrange-act-assert structure, assertion quality, coverage versus value, mutation testing, and test data management. Use when writing or reviewing any test, deciding what level to test at, diagnosing a flaky or slow suite, or when the user says 'tests', 'unit test', 'component test', 'test pyramid', 'mocking', 'flaky', or 'coverage'. This skill governs test design inside a service — cross-service contract verification belongs to contract-testing.4license: MIT5---67# Test Strategy89A test suite has one job: tell you, quickly and truthfully, whether a change is safe to ship. Most10suites fail at this in one of two ways — they are so slow and flaky that people stop believing11them, or they are so tightly bound to implementation that every refactor breaks a hundred tests12while real defects walk through.1314Both come from testing at the wrong level. The level is the decision that matters; everything else15is craft.1617## Config Resolution18191. Read `.msskills/config.yaml` in the repo root; look up `paths.testing`.202. A custom document exists at that path → read its frontmatter `mode`:21 - `override` → use it alone as the testing standard; ignore the defaults below.22 - `overlay` (default, or no `mode` key) → read [defaults](./references/defaults.md) first, then23 apply the custom document's sections on top, matched by exact heading; new sections append.243. A path is configured but no file exists there → say which path is missing, then use the defaults.254. No config file or no `paths.testing` key → use [defaults](./references/defaults.md).265. `.msskills/stack.md` exists → use its test framework, assertion style, container/stub tooling,27 and naming convention. Match the conventions of the tests already in the repository over these28 defaults.2930## Choosing the level3132Test each behaviour once, at the cheapest level that can genuinely prove it.3334| Level | Boundary | Real | Stubbed | Owns |35|---|---|---|---|---|36| **Unit** | A class or function | The code under test and its value objects | Everything with I/O | Business rules, calculations, state machines, edge cases |37| **Component** | One whole service | The service, its database, its HTTP and message entry points | Every other service | Wiring, serialisation, persistence, validation, error mapping, auth |38| **Contract** | One consumer–provider pair | The message shape | Both sides' internals | That two services agree — see `contract-testing` |39| **Integration** | One adapter and one real dependency | That adapter and that dependency | Everything else | Queries, migrations, client configuration, broker semantics |40| **End-to-end** | The deployed system | Everything | Nothing | A handful of critical user journeys, and nothing more |4142**Component tests are the highest-value level in a microservice** and the most often missing. One43service, real database in a container, real HTTP in and out, collaborators stubbed at the network44boundary: fast enough to run on every commit, and it proves the thing actually starts and works.4546## Self-Validation Checklist4748**STOP after writing or reviewing each test. Verify every check. Fix failures before presenting.**49501. **RIGHT LEVEL**: Is this the cheapest level that can prove this behaviour? A rule covered by a51 unit test does not also need an end-to-end test.522. **BEHAVIOUR NOT IMPLEMENTATION**: Would this test survive a refactor that preserves behaviour?53 If it asserts on internal calls, private state, or field order → rewrite against observable54 outcomes.553. **ONE REASON TO FAIL**: Does the test have a single clear subject? Multiple unrelated assertions56 → split, so a failure names the defect.574. **NAMED BY BEHAVIOUR**: Does the name state the scenario and the expected outcome, so a failure58 in CI is self-explanatory without opening the file?595. **AAA VISIBLE**: Are arrange, act, and assert distinguishable? Assertions interleaved with60 actions → restructure.616. **DETERMINISTIC**: Does it depend on wall-clock time, real randomness, timezone, locale,62 ordering, network, or another test's state? → inject the clock and the ID source; sort before63 comparing; seed randomness.647. **ISOLATED**: Does it pass when run alone, in a different order, and in parallel? Shared mutable65 fixtures → give each test its own data.668. **NO SLEEP**: Does it wait on a fixed sleep? → poll with a bounded timeout on the actual67 condition.689. **DOUBLES JUSTIFIED**: Is each test double replacing something slow, non-deterministic, or69 external? Mocking a pure value object or the type under test → use the real thing.7010. **ASSERTS THE POINT**: Does the assertion check what the test claims to prove — including71 values, not just that a call happened? `verify(save called)` does not prove the right thing was72 saved.7311. **FAILURE PATHS COVERED**: Are timeouts, rejections, and compensation paths tested, not just74 the happy path? Untested error handling is the code that runs during an incident.7512. **FAILS FIRST**: Would this test fail if the behaviour were removed? A test that passes against76 a broken implementation proves nothing — check it.7778All checks pass → state "Test holds: <level>, <behaviour>, deterministic."7980## Active Anti-Pattern Scan8182Any box you can check is a defect. Fix it before presenting.8384- [ ] **Ice Cream Cone**: mostly end-to-end tests, few unit tests → slow, flaky feedback; push85 coverage down to the cheapest level that proves each behaviour.86- [ ] **Mockery**: everything mocked including the subject's own collaborators, so the test asserts87 the implementation back to itself → use real objects for anything without I/O.88- [ ] **Assertion-Free Test**: exercises code and asserts nothing, or only that no exception was89 thrown → assert the outcome.90- [ ] **Sleep-Based Waiting**: `sleep(2000)` for async work → poll the condition with a timeout.91- [ ] **Shared Mutable Fixture**: tests that pass only in a particular order, or fail in parallel →92 per-test data with unique keys.93- [ ] **Snapshot Everything**: large auto-generated snapshots accepted without reading → assert the94 fields that matter.95- [ ] **Coverage Theatre**: tests written to raise a percentage, exercising getters → delete them;96 cover behaviour instead.97- [ ] **Test Logic**: conditionals and loops in tests, so the test itself needs testing → use98 parameterised cases.99- [ ] **Retried Flake**: a failing test wrapped in a retry annotation → find the non-determinism;100 a retried test is a test nobody trusts.101- [ ] **Mocked Third Party at the Wrong Layer**: mocking the vendor SDK rather than stubbing its102 HTTP → stub at the network boundary so serialisation is exercised.103- [ ] **Happy Path Only**: no test for timeout, rejection, duplicate delivery, or compensation.104- [ ] **Slow Unit Suite**: unit tests that start a container or a framework context → that is a105 component test; move it.106- [ ] **Disabled Test**: `@Ignore`/`skip` with no linked issue → fix or delete; a skipped test is107 false comfort.108109## Ambiguity Signals110111Route these through `collaborative-judgment`. Each has two defensible answers.112113- **Unit or component.** A rule reachable only through the HTTP layer can be unit tested by114 extracting it, or component tested where it lives. Extraction is cleaner and is also a design115 change.116- **How many end-to-end tests.** They catch integration and environment faults nothing else does,117 and they are the slowest and flakiest thing you own. The right number is small and non-zero.118- **Test doubles for another service.** A stub is fast and can drift from reality; a real instance119 in a container is faithful and slow. Contract tests are how you get both, at the cost of a broker120 (see `contract-testing`).121- **Coverage targets.** A number drives behaviour, and not always the behaviour you wanted. Mutation122 score measures the same thing more honestly and costs more to run.123- **Fixtures versus builders.** Shared fixtures are concise and couple tests together; per-test124 builders are explicit and verbose.125- **Whether to test a thin adapter.** Some layers are pure delegation, and a test for them asserts126 the framework works.
Run npx skillmds@latest add parvez3019/test-strategy in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Choose the right test level for each behaviour and write tests that fail only for real reasons. Covers the microservice test portfolio — unit, component (service in isolation with real infrastructure and stubbed collaborators), integration, and narrow end-to-end — plus what each level must not own, test doubles discipline, determinism and flakiness, arrange-act-assert structure, assertion quality, coverage versus value, mutation testing, and test data management. Use when writing or reviewing any test, deciding what level to test at, diagnosing a flaky or slow suite, or when the user says 'tests', 'unit test', 'component test', 'test pyramid', 'mocking', 'flaky', or 'coverage'. This skill governs test design inside a service — cross-service contract verification belongs to contract-testing. It is listed under Web & Frontend on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free. This skill is licensed under MIT.
parvez3019 (@parvez3019) published this skill. Their other Agent Skills are listed on their SkillMD profile.