跳到正文
cccskills
免费在 GitHub 公开

unit-testing

Small-test style for unit tests — stable boundary, data over mocks, focused assertions, concise makeMe (all packages). Use when writing or changing unit tests in any package.

中文简介正在准备中。当前显示原文说明。

nerds-odd-e/doughnut星标 50(GitHub 50 + cccskills 0)

实测评分 未实测

查看安装方法

包含的文件(1)

  • SKILL.md7.6 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

"Small test" style

This repo’s automated tests are E2E or unit tests — nothing in between. Write unit tests in the "small test" style below.

A "small test" means: a fast, in-process unit test (JUnit, Vitest, Bach, …) that drives a stable boundary with crafted data/makeMe, exercises real lower-level production code, and mocks only external dependencies. Stack commands, entry points, and tooling stay in the package skills (frontend / frontend-testing, backend / backend-testing, cli, mcp-server).

Stable boundary

  • Drive the outermost (or domain-stable) interface. Prefer the public surface under test — e.g. a controller, mounted component, CLI run / runInteractive, MCP tool — or an interface that is itself a deliberate, domain-stable contract (pure algorithm, validation API). Do not add a test class per internal production class, and do not widen exports only so tests can reach helpers.
  • Cover lower layers with data, not with more test surfaces. Craft realistic, scenario-specific preconditions and fixtures so one boundary test exercises the real lower-level production routines. Prefer that over isolating each collaborator behind mocks or thin wrapper tests.
  • Build those fixtures with makeMe — keep construction concise (see below).
  • Do not mock unless external or exceptional. Mock only true external dependencies (third-party APIs, network services, and the like) or extremely rare special cases. Package rules name the allowed exceptions (OpenAIClient, frontend mockSdkService for the backend HTTP API, CLI spies on donut-api, etc.). Prefer the real database, real in-process collaborators, and real browser rendering inside the system under test.
  • Loud failures — Code that is allowed to fail loudly is not a unit-test subject for that failure. Policy: ADR 0006 Usage.

Focused assertions

  • One behavior per test. Assert only what this scenario uniquely establishes.
  • Canonical shape once. Shared post-conditions belong in one test. Sibling scenarios assert only their delta.
  • Different precondition ≠ re-assert unrelated post-condition. If outcome X is clearly independent of what changed in the setup, do not repeat X.
  • Prefer asserting the positive signal over “not the other message/alert” when the positive already distinguishes the case.
  • When a change removes something, the tests are those of the behavior that survives or replaces it. Write no test, assertion, fixture, or mock that observes the removed thing (not called, absent, ignored). Apply removal proof to each proof item. Rule: principle 7 in AGENTS.md / CLAUDE.md.
  • In loops (thresholds, retries, frame polls), assert the loop result, not the same intermediate outcome every iteration.
// ❌ every related case repeats the full payload
assertFalse(result.getAnswer().getCorrect());
assertThat(result.getAnswer().getOutcome(), is(ACCIDENTAL_MATCH));
assertThat(result.getMatchedNotes(), hasSize(1));

// ✅ canonical case asserts the shape; this case only the delta
assertThat(result.getAnswer().getOutcome(), is(ACCIDENTAL_MATCH));
// ❌ re-check section when the unique claim is “no link CTAs”
expect(wrapper.find('[data-testid="matched-notes-section"]').exists()).toBe(true)
expect(wrapper.findAll('[data-testid^="wiki-link-or-relationship-to-matched-note-"]')).toHaveLength(0)

// ✅
expect(wrapper.findAll('[data-testid^="wiki-link-or-relationship-to-matched-note-"]')).toHaveLength(0)

Removal proof

For a mixed change, inspect the individual assertions and observations in the proof table, slice Proof, and implementation brief. A scope statement about removal does not make a contradictory proof item acceptable.

  • Surviving or replacement behavior: retain the proof of button states, error feedback, and other promised outcomes, and carry it through all three handoff forms. A negative assertion can prove a surviving business outcome, such as silent completion; classify by what it establishes, not its syntax.
  • Actively enforced refusal: retain its rejection proof, including a read-only user's unavailable editing control. This establishes authorization behavior the product still promises.
  • Removed behavior or interface: delete its assertions and the fixtures, helpers, and mocks used only by them. Do not replace them with assertions that it is absent, not called, or ignored.
  • Sweep: record the one-time acceptance search and classified result as a reading in the plan, separately from surviving-behavior proof. Create no permanent absence test or checker.

The coordinator corrects inherited contradictions before delegation. An implementer or refactorer encountering one skips and reports that item while preserving valid proof; accepting a removal must not discard its surviving behavior's tests.

Destructive operations (hard deletes, delete migrations)

The concise-fixture guidance below is correct for behavior tests. For hard deletes and delete migrations, fixture completeness beats minimality — a bare target row with no children only proves row selection, not referential fan-out.

  • Hard-delete endpoints: build a row in every table that holds an FK into the deleted entity (directly or via CASCADE). Example: MemoryTrackerDeleteControllerTest uses makeMe.aConversation().forARecallPrompt(...) so deleting a tracker exercises the conversation → recall_prompt edge, not just the tracker row.
  • Delete migrations: same rule — seed every child table in the FK closure, not only rows the WHERE clause selects. While a gated migration is pending, pair it with a focused temporary test for the placeholder and row selection; remove that migration-only harness after confirmed production application.
  • Schema guard: DeletableEntityFkClosureTest catches restricting FKs without fixtures; it does not replace completeness tests for runtime delete paths.

Concise makeMe setup

  • Build fixtures with makeMe (donut-test-fixtures/makeMe on TS; backend MakeMe on Java). Rely on builder defaults when a value is unused in assertions and does not affect the logic under test. Exception: destructive operations above — do not minimize away FK children.
  • Do not create users, notebooks, or owners only to satisfy wiring if the builder already defaults them and the test never refers to them.
  • Prefer ownership helpers so dependents inherit the owner (e.g. backend notebookOwnedBy → tracker .by default).
  • Prefer builder APIs over post-construction mutation (e.g. .id(10), not note.id = 10).
  • Prefer domain-shaped helpers over field soup when the same shape repeats (e.g. .accidentalMatch(...), .overlap(...), .aliases(...)).
  • If a mandatory dependency or repeated fixture cannot be expressed concisely, extend the makeMe builder — do not leave verbose setup in every test.
// ❌
Notebook nb = makeMe.aNotebook().creatorAndOwner(currentUser.getUser()).please();
Note n = makeMe.aNote().notebook(nb).title("X").please();
makeMe.aMemoryTrackerFor(n).by(currentUser.getUser()).spelling().please();

// ✅
Note n = makeMe.aNote().notebookOwnedBy(currentUser.getUser()).title("X").please();
makeMe.aMemoryTrackerFor(n).spelling().please();
// ❌
const note = makeMe.aNote.please()
note.id = 10
note.noteTopology.id = 10

// ✅
const note = makeMe.aNote.id(10).please()

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

backend

免费

Backend Java — Spring Boot controllers/services, JUnit controller tests, MakeMe. Read this first for any backend work. Use when working on the Java Spring backend.

中文简介正在准备中。当前显示原文说明。

nerds-odd-e/doughnut502026年10月26日 更新

Backend Java production code, Spring Boot controllers/services/repositories, controller return values, imports, schema ERD pointer. Use when editing backend production Java.

中文简介正在准备中。当前显示原文说明。

nerds-odd-e/doughnut502026年10月26日 更新

Backend Java tests, controller-level stable-boundary tests, JUnit, @Nested, MakeMe builders, @Transactional, parameterized tests. Use when writing or changing backend JUnit tests.

中文简介正在准备中。当前显示原文说明。

nerds-odd-e/doughnut502026年10月26日 更新

cli

免费

related to donut CLI. Use when working on donut-cli.

中文简介正在准备中。当前显示原文说明。

nerds-odd-e/doughnut502026年10月26日 更新

Set up and run the Donut dev environment on Cursor Cloud VM (no Nix). Use when working on Cloud VM, running tests on Cloud VM, or encountering command errors related to missing nix. Triggers on: cloud VM, cloud agent, no nix available, /workspace path.

中文简介正在准备中。当前显示原文说明。

nerds-odd-e/doughnut502026年10月26日 更新

Regenerate docs/database-erd.md from MySQL information_schema (Mermaid ERD: foreign keys and key columns). Use after Flyway schema changes, new migrations, or when the diagram is stale. Triggers on: database ERD, database-erd.md, export_database_erd, schema diagram, ER diagram after migration.

中文简介正在准备中。当前显示原文说明。

nerds-odd-e/doughnut502026年10月26日 更新

查看 nerds-odd-e 的全部技能

报告此技能的问题