Testing
Executable checks for contracts, edge cases, regressions, and useful failure messages.
52 items in this guidance cluster.
Tagged guidance
Guide item
Agent Workflow rules
Prefer Build-Preserving EditsAGENT-PREFER-BUILD-PRESERVING-EDITSMake multi-step edits in slices that keep compilation or tests close to green when the route allows it. Build-preserving work keeps failures close to the edit that caused them.Report Proof in HandoffsAGENT-REPORT-PROOF-IN-HANDOFFSReplace confidence language with the exact checks, inspection, screenshots, and skipped validation behind a handoff. Proof lets reviewers decide what to trust and what remains risky.
Boundary rules
Avoid Global Mutable StateBOUNDARY-AVOID-GLOBAL-MUTABLE-STATEKeep shared process state behind explicit owners, handles, synchronization, and reset policy. This preserves test isolation and lifecycle reasoning while still allowing deliberate globals such as caches or registries when their contract is visible.Make Ambient Inputs ExplicitBOUNDARY-MAKE-AMBIENT-INPUTS-EXPLICITPass time, randomness, environment, locale, terminal size, network clients, and process state through visible inputs when they affect behavior. Injecting only the relevant ambient values improves tests and portability without spreading oversized context objects.Separate Pure Core From EffectsBOUNDARY-SEPARATE-PURE-CORE-FROM-EFFECTSMove domain computation away from rendering, I/O, mutation, and global state when effects obscure the decision being tested. The split gives tests a stable surface, but should be skipped when it adds indirection without a useful boundary.Treat Terminal UI As Product SurfaceBOUNDARY-TREAT-TERMINAL-UI-AS-PRODUCT-SURFACETreat terminal layout, input, scroll behavior, color, viewport size, and platform differences as a user-facing contract when people rely on the interface. This makes regressions reviewable without requiring full visual testing for every tiny internal tool.
Change Shape rules
Performance rules
Rust rules
Contain UnsafeRUST-CONTAIN-UNSAFEKeep unsafe blocks small, wrapped by safe APIs, documented, and tested through safe behavior. Localized obligations make the safety argument auditable.Hide Test-Only HelpersRUST-HIDE-TEST-ONLY-HELPERSKeep fixtures and shortcuts behind test-only modules, features, or support crates unless they are deliberate API. This prevents scaffolding from leaking into production contracts.Inject Host Interactions at BoundariesRUST-INJECT-HOST-INTERACTIONS-AT-BOUNDARIESPass filesystem, network, time, randomness, and process behavior through boundaries when tests or alternate environments need control. This keeps the core deterministic and effects explicit.Keep CI High SignalRUST-KEEP-CI-HIGH-SIGNALKeep required Rust checks strict, fast, deterministic, and actionable. Slow or flaky ritual trains maintainers to ignore failures while real drift accumulates.Name Tests By BehaviorRUST-NAME-TESTS-BY-BEHAVIORName tests after the behavior, boundary, or regression they protect so failure output is useful before a reader opens the body. Keep names concise and let module context carry repeated setup details.Run Feature Gated ValidationRUST-RUN-FEATURE-GATED-VALIDATIONExercise the feature combinations touched by a Rust change so gated code, docs, and integrations actually build. Choose representative combinations when exhaustive feature matrices would be too expensive.Validate Builders On BuildRUST-VALIDATE-BUILDERS-ON-BUILDValidate cross-field builder invariants in build where partial configuration becomes a usable value. Keep build infallible only when defaults and setters make invalid states impossible.Validate Rust Docs As CodeRUST-VALIDATE-RUST-DOCS-AS-CODETreat Rust documentation examples, links, feature assumptions, and generated README content as code that must be checked. Use docs builds, doctests, feature-gated checks, and Markdown lint according to the changed surface.Validate Unsafe Through Safe APIRUST-VALIDATE-UNSAFE-THROUGH-SAFE-APITest unsafe internals through the safe API wrapper that callers rely on. Internal unsafe tests and tools such as Miri should support, not replace, proof that safe calls uphold the contract.
Test Failures rules
Avoid Opaque Boolean AssertionsTEST-AVOID-OPAQUE-BOOLEAN-ASSERTIONSPrefer comparisons or richer assertions when a predicate hides state needed to diagnose the failure. The first failure should show the useful actual value.Optimize Failure OutputTEST-OPTIMIZE-FAILURE-OUTPUTDesign tests so failures include expected values, actual values, inputs, and contract context where that helps repair. Useful output shortens CI and agent debugging loops.Split Unrelated AssertionsTEST-SPLIT-UNRELATED-ASSERTIONSSplit assertions that diagnose different behaviors when one failure would hide another. Keep checks together only when they express one contract more clearly as a group.
Testing rules
Check Important Feature CombinationsTEST-CHECK-IMPORTANT-FEATURE-COMBINATIONSExercise default, disabled-default, all-feature, and important feature-pair builds. Feature flags change APIs and compile paths, so use a risk-based matrix instead of every combo.Check Maintainer Commands in CITEST-CHECK-MAINTAINER-COMMANDS-IN-CIPut documented maintainer commands, or intentionally stronger equivalents, in CI. This keeps local instructions honest while leaving slow or credentialed checks to special jobs.Check MSRV and PlatformsTEST-CHECK-MSRV-AND-PLATFORMSRun checks for declared Rust versions and supported platforms when they are promised. Compatibility claims are public contracts, so skip matrices only when no such claim exists.Choose Validation by RiskTEST-CHOOSE-VALIDATION-BY-RISKMatch the amount and kind of validation to the changed surface and failure cost. Cheap checks come first, but risky or uncertain behavior needs targeted evidence.Cover Async Routing Edge CasesTEST-COVER-ASYNC-ROUTING-EDGE-CASESTest late replies, unrelated input, timeouts, and unmatched async responses. These cases protect request correlation and cleanup without relying on wall-clock luck.Cover Local Logic with Unit TestsTEST-COVER-LOCAL-LOGIC-WITH-UNIT-TESTSUse unit tests for small local logic such as parsing helpers and policy branches. They give fast precise feedback, while boundary behavior still needs higher-level tests.Cover Navigation BoundariesTEST-COVER-NAVIGATION-BOUNDARIESExercise first, last, empty, oversized, and repeated-navigation states in tests. Boundary cases catch cursor and scrolling bugs that polished manual demos often miss.Cover Policy OutcomesTEST-COVER-POLICY-OUTCOMESTest allowed, denied, redacted, fallback, and unsupported policy outcomes. Policy value lives at decision boundaries, so assert caller-visible behavior instead of internals.Cover Public Boundaries with Integration TestsTEST-COVER-PUBLIC-BOUNDARIES-WITH-INTEGRATION-TESTSUse integration tests to prove public module, crate, or adapter boundaries. They catch composition failures unit tests miss, while local logic can stay unit-tested.Cover Public Examples with DoctestsTEST-COVER-PUBLIC-EXAMPLES-WITH-DOCTESTSCompile public documentation examples as doctests when they do not need fragile state. Executable examples catch stale guidance, reserving no-run or ignored examples for real limits.Fuzz Parsers, Formatters, and State MachinesTEST-FUZZ-PARSERS-FORMATTERS-AND-STATE-MACHINESUse fuzzing or property tests for input-heavy parsers, formatters, and state machines. Large input spaces hide failures, so keep long fuzzing outside PR gates unless it is stable.Keep Drift Claims AlignedTEST-KEEP-DRIFT-CLAIMS-ALIGNEDTie support claims, fixtures, examples, docs, and API paths to executable drift checks. Stable claims need evidence, but wording-only changes should not break broad tests.Keep Slow Checks out of PR CITEST-KEEP-SLOW-CHECKS-OUT-OF-PR-CIKeep long fuzzing, exhaustive matrices, and noisy benchmarks out of required PR CI. Fast gates preserve review flow, while heavier checks belong in release or scheduled validation.Match Evidence to SurfaceTEST-MATCH-EVIDENCE-TO-SURFACEValidate the actual changed surface, such as rendered docs, API behavior, or byte output. The narrowest relevant proof is more persuasive than unrelated broad test success.Prefer Deterministic TestsTEST-PREFER-DETERMINISTIC-TESTSPrefer tests controlled by fixed inputs, clocks, ordering, and local state. Deterministic failures are reproducible, while real integration checks should be isolated by cost.Prove Command Construction and DisplayTEST-PROVE-COMMAND-CONSTRUCTION-AND-DISPLAYTest both executable command shape and displayed command text when users rely on them. Quoting, redaction, ordering, and platform formatting can fail even when a local happy path works.Prove Contracts Not TriviaTEST-PROVE-CONTRACTS-NOT-TRIVIAWrite tests around observable contracts instead of private helper trivia. This preserves refactoring freedom unless the detail is itself the promised behavior, such as a stable user-facing output contract.Run Docs as a First-Class GateTEST-RUN-DOCS-AS-FIRST-CLASS-GATETreat documentation checks as real validation for examples, links, commands, and claims. Prose-only edits may need less proof, but API-facing docs should fail before stale guidance ships.Run Fast Format and Lint Gates EarlyTEST-RUN-FAST-FORMAT-AND-LINT-GATES-EARLYRun cheap format and lint gates early in the feedback loop. They remove mechanical failures quickly, but they do not replace validation of risky behavior.Use Realistic Parser SamplesTEST-USE-REALISTIC-PARSER-SAMPLESTest parsers with representative input, malformed cases, and safe degradation examples. Real samples catch compatibility failures, but fixtures should be minimized and scrubbed.Validate Declared Minimum Dependency VersionsTEST-VALIDATE-DECLARED-MINIMUM-DEPENDENCY-VERSIONSCheck that declared minimum dependency versions still build the supported behavior. Lockfile tests can hide newer API usage, so use targeted minimal-version checks when needed.Write Regression Tests for Bug FixesTEST-WRITE-REGRESSION-TESTS-FOR-BUG-FIXESAdd a test that fails before the bug fix and protects the repaired contract. Skip only when reproduction is impractical, and then record the closest trustworthy validation.
Pattern items
Characterize Then Fixcharacterize-then-fixBefore fixing incorrect behavior, add a passing test that records the observed result and labels it as known incorrect. Then fix the implementation, update that same expectation to the intended contract, and rewrite the rationale as durable regression context.Inject Time And Randomnessinject-time-and-randomnessDirect reads of clocks and random sources make behavior hard to reproduce, test, and explain. Pass time and randomness through explicit seams at the boundary where nondeterminism is chosen.Keep Async Boundaries Explicitkeep-async-boundaries-explicitHidden async work obscures cancellation, ordering, resource ownership, and failure behavior. Make task spawning, awaiting, retries, and background ownership visible at the boundary where callers need to reason about them.Smallest Trustworthy Verificationsmallest-trustworthy-verificationVerification is weak when it is either skipped or broad without covering the likely failure. Run the cheapest check that could catch the risk introduced by the change, broadening only when shared contracts or user-visible behavior require it.Test Observable Behaviortest-observable-behaviorTests that pin private structure can block useful refactors while missing broken user outcomes. Test observable outputs, errors, side effects, state, events, and boundaries, reserving private tests for dense rules that are otherwise hard to reach.Verify With Canaries Before Cutoververify-with-canaries-before-cutoverAutomation is risky when production cutover is the first real proof. Use a canary path, rollback plan, and human approval for operations with remote access, credentials, production data, or long-lived service impact.
Principle items
Measure Before Optimizingmeasure-before-optimizingPerformance work needs a workload, baseline, goal, change, and comparison. Evidence keeps optimization from becoming readability, dependency, or API churn without a proven payoff.Tests Should Explain Failurestests-should-explain-failuresA failing test should point at the broken contract, not merely report that a predicate was false. Choose assertion shapes that expose the relevant value, variant, order, or case name.