Cheatsheets
Testing strategy
Test levels, the pyramid vs the trophy, doubles, flaky-test fixes, and Vitest & Playwright.
Visualize
Testing — the pyramid & the TDD loop
Many fast unit tests at the base, few slow E2E at the top — plus the red → green → refactor loop.
expect(add(2, 3)).toBe(5) // ✗ fails
Write a failing test first — it defines the goal.
Lean on the base: many fast unit tests, fewer slow E2E. Each is written red → green → refactor.
68 entries
Test levels5
Unit testTests one function/module in isolation, dependencies stubbed.
Integration testTests several real units together (e.g. service + DB + parser).
E2E (end-to-end) testDrives the whole app like a user — browser, server, DB.
Contract testVerifies a producer and consumer agree on an API/message shape.
Speed vs confidenceLower tests = faster, cheaper, less realistic; higher = slower, more confident.
Strategy & shape4
Test PyramidMany unit, fewer integration, very few e2e (Mike Cohn).
Testing Trophystatic → unit → integration (most) → e2e (Kent C. Dodds).
Why integration wins on ROIIntegration tests resemble how software is used, with manageable cost.
Ice-cream cone (anti-pattern)Inverted pyramid: lots of manual + e2e, few unit tests.
Test doubles9
Test double (umbrella term)Any stand-in for a real dependency: dummy, stub, spy, mock, or fake.
DummyA placeholder passed to satisfy a signature; never actually used.
StubReturns canned answers to calls made during the test.
SpyRecords how it was called (args, count) while optionally passing through.
MockA double with pre-programmed expectations; the assertion is on the interaction.
FakeA working but simplified implementation (e.g. in-memory DB).
Mock vs fakeMock = scripted calls + assertions; fake = a real (simpler) implementation.
Over-mocking (pitfall)So many mocks the test asserts implementation, not behavior.
Classicist vs mockistClassicist tests state with real objects; mockist tests interactions with mocks.
Structure & style8
Arrange-Act-Assert (AAA)Three phases: set up, do the thing, check the result.
Given-When-ThenBDD phrasing of AAA: precondition, action, expected outcome.
One logical assertion per testEach test verifies a single behavior, not a grab-bag of checks.
Descriptive test namesName the behavior, not the method: 'returns 401 when token expired'.
Fixtures vs factories vs buildersThree ways to produce test data, trading reuse for clarity.
Test data managementKeep data minimal, intention-revealing, and isolated per test.
beforeEach / afterEachRun setup before, and teardown after, every test in a block.
Test isolationNo test depends on another's state or execution order.
Vitest (runnable)10
describe / it / expectGroup tests, declare a case, assert on a value.
Common matcherstoBe / toEqual / toMatchObject / toThrow / resolves / rejects.
vi.fn()Create a mock/spy function you control and assert on.
vi.spyOn(obj, "method")Wrap an existing method to record calls (and optionally replace it).
vi.mock("./module")Replace an entire imported module with a mock.
vi.useFakeTimers() + advanceTimersByTimeTake control of time so timers fire instantly and deterministically.
test.each([...])Parameterised test — run the same body over a table of cases.
beforeEach (Vitest)Reset state/mocks before every test for isolation.
vitest run --coverageRun the suite once and report coverage (line/branch/function).
Full example test fileA small, complete Vitest file you can drop in and run.
Playwright (runnable)9
test / expect (@playwright/test)Declare a browser test; assert with auto-retrying expect.
Locators: getByRole / getByText / getByTestIdFind elements the way a user (or assistive tech) would.
await expect(locator).toBeVisible()Web-first assertion — auto-waits and retries until it passes or times out.
test.step("name", async () => {...})Group actions into a named step for readable traces/reports.
page.route() — network mockingIntercept requests and serve a canned response.
Fixtures (Playwright)Reusable, isolated setup injected per test (page, context, custom).
Trace / screenshot on failureCapture a replayable trace and screenshot when a test fails.
Parallelism & projectsRun tests in parallel and across multiple browser configs.
Full example e2e fileA small, complete Playwright spec you can drop in and run.
Coverage & quality4
Line vs branch vs statement coverageDifferent things 'coverage %' can mean — branch is the strict one.
Coverage ≠ quality (Goodhart)Code can be 100% covered and still be wrong — coverage measures execution, not correctness.
Mutation testing (Stryker)Inject bugs ('mutants') and check your tests catch them — a real quality signal.
FIRST principlesFast · Isolated · Repeatable · Self-validating · Timely.
Flaky tests10
Flaky test (definition)A test that passes and fails on the SAME code without changes — non-deterministic.
Async not awaitedCause: the test finishes before the async work settles.
Time / Date.now()Cause: real clocks make tests time-of-day or timezone dependent.
RandomnessCause: Math.random / uuid / shuffles make outcomes vary run to run.
Test order / shared stateCause: one test leaves state that changes another's result.
NetworkCause: tests hit a real, slow, or rate-limited service.
AnimationsCause: CSS transitions race the assertion in e2e tests.
Race conditionsCause: assertion fires before the UI/data reaches its final state.
Sleeps vs auto-waitingReplace arbitrary waits with assertions that retry until the condition holds.
Retries (last resort) + quarantineRetries hide flakes; quarantine isolates them. Neither is a fix.
TDD2
Red-green-refactorWrite a failing test, make it pass minimally, then clean up.
When TDD helps / doesn'tShines on clear specs and bug fixes; awkward for exploratory UI work.
Anti-patterns7
Testing implementation detailsAsserting on internals (private methods, exact calls) instead of behavior.
Snapshot abuseGiant auto-generated snapshots that everyone blindly updates.
Slow unit tests'Unit' tests that hit a DB, network, or filesystem — really slow integration tests.
Shared mutable fixturesA fixture mutated by one test, corrupting others.
Asserting on logsVerifying behavior by checking log output instead of real effects.
No-assert testA test that runs code but never asserts — green even when broken.
Conditional logic in testsif/else, loops, or try/catch deciding what a test asserts.