testing
GitHub提供语言无关的高质量测试编写规范,强调仅测试公共API、禁止打补丁或Mock、使用Fakes集成模块、优先属性及黄金文件测试,旨在提升测试鲁棒性并避免脆弱测试。
Trigger Scenarios
Install
npx skills add uw-syfi/vibesys --skill testing -g -y
SKILL.md
Frontmatter
{
"name": "testing",
"metadata": {
"kind": "generic"
},
"description": "Write, change, review, or fix tests in any language. Use when adding or editing any test, fixing a bug (regression test), tempted to patch or mock, adding or changing a Fake, writing property-based, golden-fixture, or fault-injection tests, testing code that calls agents, clusters, or subprocesses, or diagnosing a flaky test."
}
Testing
Write high-value tests: tests that keep passing through refactors and fail only when observable behavior changes. A test is low value if renaming a private function, splitting a module, or swapping an implementation detail breaks it.
These rules are language-independent and codebase-independent. Language idioms live in a per-language reference; read the one for the language you are editing. A codebase's own harness (its tiers, simulation library, checks and commands) belongs in that codebase's own skill; load it too.
- references/python.md
- references/typescript.md
- Another language: apply the same rules with that language's idioms, and add a reference file for it in the same shape.
Rules
- Test the public API only. The public API is the surface other packages
or modules are already allowed to depend on: a library's
apientry point, the module API that sibling modules use inside a package, and the application's facade or serialized events at the top. Do not import private modules or assert on private state. The one exception is a test whose point is a tricky implementation detail; mark that sitetest-isolation: <specific reason>in a comment. - No patching or mocking. Do not replace code at runtime (monkeypatching attributes, module mocking, spies, mock frameworks). It couples the test to how the code is written. Setting inputs is fine: environment variables, working directory, temp directories. If a test seems to need a patch, the code is missing a seam: inject the owning library's interface through a constructor argument or parameter, and pass a Fake implementation.
- Integrate modules with Fakes. A Fake is an in-memory, faithful implementation of the same API, not a stub with canned answers. A Fake is never more capable or more forgiving than production: it reports the same supported capabilities (derived from the same definition), validates and loads state as strictly, and enforces the same limits. Whoever changes the real behavior updates the Fake in the same PR. The owning library ships one contract suite per interface; run it against every implementation, Fake and production, without skipping contract cases. See references/fakes-and-contracts.md.
- Test properties, not instances. Default to property-based tests and fuzzing for parsers, serializers, validators, pure logic, and state machines. Use single examples only for a named scenario or a regression. Use golden fixtures where behavior reduces to a deterministic state snapshot. Test functional cores with pure properties over generated event sequences, including crash and replay at every durable intent boundary. Use no async, sleeps, or Fakes in core tests. See references/properties-and-goldens.md.
- No flaky tests. A test's result must never depend on timing. Never depend on timeouts, sleeps, wall-clock time, scheduling order, or shared state. Inject clocks and simulate timeouts; do not wait for them. Every wait on something the test does not control needs a bound, and that bound only guards against a hang: a passing run never gets near it, and it is never what the test synchronizes on. Never add retries. Zero tolerance: on seeing any flake, yours or not, root-cause it, redesign the test to remove the source, and leave it better than you found it. Ship the fix as its own PR, then rebase your current PR on top of it. See references/flakiness.md. Tiers. Unit and composition tests are the deterministic tiers: they use no real time, threads, processes, sockets or signals, no nonzero sleeps, no unbounded waits or joins, and never signal their own process, except through a deterministic simulation layer (a virtual clock, gates, an in-process child runner, Fakes). Only a few named real tiers drive real systems. Product code takes the simulation layer's interfaces instead of the real APIs, so tests can substitute them. Enforce this with a check whose baseline of existing uses only shrinks.
- Bug fixes. Add a regression test at the lowest layer that reproduces the
symptom, through the public API, that fails at the merge base. Then add a
property test that generalizes the pattern, so the whole class of bug is
covered and not just the reported instance. Example: for an uppercase
hypothesis id that broke workspace names, test every agent-supplied id with
a property test, not only the uppercase one. The class is the mechanism
named under the
software-designskill's Mechanism checkpoint; a test that replays the one reply or exit path that triggered the bug does not cover it. Mark a property-based regression test@pytest.mark.regressionand pin the reported input with@example(...): CI draws about 10 derandomized examples, which may never hit the bug. The checktest_every_property_regression_test_pins_its_inputfails a@giventest marked, named or documented as a regression (or citing#NNNN) that has no@example; its baseline of older violations only shrinks. A flaky test whose defect is in the test itself is not a product bug: fix the test and show the deterministic reproduction in the PR, without a test of the test (see references/flakiness.md). - Test the unhappy paths at boundaries. Canned replies that match what the code expects test only the happy path. Where code meets an agent, a cluster, an MCP client, or a subprocess, drive it with generated behavior: agent output generated from the declared schema, including options the run does not offer, repeated mistakes, malformed output, and replies slower than the caller's deadline; and scheduled faults on the far side. For a scope that owns a resource, test as a property that an exit at any point (each step, exception, cancellation, signal, stop) releases the resource and ends in a typed terminal state. Composed-system chaos tests inject implementation failures and unknown exceptions; require a typed terminal state, never an implicit success. Test an agent tool server with synthesized tool calls and call sequences, not agents. Assert invariants, not one expected trace. See references/fault-injection.md.
- Test process and signal code in process. Logic that runs in a child process or a signal handler is invisible to coverage and slow to test through subprocesses. Put it behind a function the test calls directly with injected I/O interfaces; keep at most one subprocess test per entry point to prove the wiring.
Before handing back
- Run the narrowest test target first, then broaden.
- Run the codebase's test-isolation check if one exists. It is a shrink-only ratchet: existing violations are baselined, new ones fail, and removing sites from a baselined file means lowering its recorded counts.
- Do not migrate unrelated tests to these rules. Apply them to new tests and to the tests you are already changing.
Version History
-
256ebe6
Current 2026-10-18 21:28
将仓库特定的配置和路径从通用技能中剥离,分离为专门的代码库技能;要求基于属性回归测试必须包含示例标记。
- 21be1ce 2026-09-27 15:19


