Skip to main content

testing

Apply Epicenter test-file conventions for setup, factories, Result assertions, type tests, naming, and pruning. Use when writing, reviewing, deleting, or reorganizing tests, especially `*.test.ts` files.

来源信息

仓库
EpicenterHQ/epicenter
最近来源活动
2026年8月26日 20:30
检测到的 SKILL.md 语言
英语
星标
4,817
分支
385

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

文件资源管理器
6 个文件

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
testing
description
Apply Epicenter test-file conventions for setup, factories, Result assertions, type tests, naming, and pruning. Use when writing, reviewing, deleting, or reorganizing tests, especially `*.test.ts` files.
metadata
{"author":"epicenter","version":"2.0"}
# Test File Conventions ## References Load these on demand based on what you're working on: - If working with **negative type tests** (`@ts-expect-error`, `bun:test` type strategy, no `as any`), read [references/type-testing.md](references/type-testing.md) - If working with **test setup architecture** (`setup()` patterns, composable setup, `beforeEach` avoidance, shared schemas), read [references/setup-pattern.md](references/setup-pattern.md) - If working with **test organization structure** (flat tests, `describe()` boundaries, helper-over-nesting), read [references/test-structure.md](references/test-structure.md) - If **auditing existing tests** for hedged assertions, pass-through getters, stalled fakes, dead fake surface, or docstrings that contradict the code, read [references/honest-tests.md](references/honest-tests.md) - If **deleting or pruning tests** that may not earn their keep, read [references/test-deletion-grill.md](references/test-deletion-grill.md) External reading: - Kent C. Dodds, ["Avoid Nesting When You're Testing"](https://kentcdodds.com/blog/avoid-nesting-when-youre-testing) : setup functions over beforeEach, flat tests - Kent C. Dodds, ["AHA Testing"](https://kentcdodds.com/blog/aha-testing) : avoid hasty abstractions in tests - Kent C. Dodds, [Testing JavaScript](https://testingjavascript.com) : Test Object Factory Pattern - Matt Pocock, ["How to test your types"](https://www.totaltypescript.com/how-to-test-your-types) : vitest `expectTypeOf` for type testing - Matt Pocock, [`shoehorn`](https://github.com/total-typescript/shoehorn) : partial mocks for test ergonomics > **Related Skills**: See `services-layer` for the service patterns being tested. See `typescript` for type testing conventions. ## Result Assertions When a test asserts a wellcrafted `Result`, use `expectOk` and `expectErr` from `wellcrafted/testing` instead of hand-rolled error checks. ```ts import { expectErr, expectOk } from 'wellcrafted/testing'; const data = expectOk(await service.doThing()); expect(data.id).toBe('1'); const error = expectErr(await service.doThing({ invalid: true })); expect(error.name).toBe('InvalidInput'); ``` Avoid local helper clones, `expect(error).toBeNull()` success checks, and `if (error) throw ...` unwrapping when the value is a wellcrafted `Result`. This rule does not apply to plain response bodies, UI snapshots, or other objects that merely have an `error` property. ## Tests vs. Benchmarks Two distinct file extensions, two distinct purposes: - **`*.test.ts`** : asserts behavior with `expect()`. Runs under `bun test` (repo default, CI). A test file without at least one `expect()` call does not belong under this extension. - **`*.bench.ts`** : measures and reports. Prints tables, timings, or storage sizes. Runs under `bun bench` only. No assertions required (perf thresholds on shared hardware flake; prefer visual trends). A single file is one or the other, never both. Benchmarks live under `src/__benchmarks__/` within a package; tests are colocated with the module they cover. The `bun test` default-discovery glob picks up only `*.test.ts` and friends, so renaming a report from `.test.ts` → `.bench.ts` is what excludes it from CI. ## File-Level Doc Comments Every `.test.ts` file MUST start with a JSDoc block explaining what is being tested and the key behaviors verified. This serves as documentation for the module's contract. ### Structure ```typescript /** * [Module Name] Tests * * [1-3 sentences explaining what this file tests and why these tests matter.] * * Key behaviors: * - [Behavior 1] * - [Behavior 2] * - [Behavior 3] * * See also: * - `related-file.test.ts` for [related aspect] */ ``` ### Good Example ```typescript /** * Cell-Level LWW CRDT Sync Tests * * Verifies cell-level LWW conflict resolution where each field * has its own timestamp. Unlike row-level LWW, concurrent edits to * DIFFERENT fields merge independently. * * Key behaviors: * - Concurrent edits to SAME field: latest timestamp wins * - Concurrent edits to DIFFERENT fields: BOTH preserved (merge) * - Delete removes all cells for a row */ ``` ### Bad Example (Too Minimal) ```typescript // Tests for create-tables ``` ### Section Headers For long test files (100+ lines), use comment headers to separate logical sections: ```typescript // ============================================================================ // MESSAGE_SYNC Tests // ============================================================================ ``` ## Multi-Aspect Test File Splitting When a module has distinct behavioral aspects, split into focused test files rather than one monolithic file: | Pattern | Use Case | | ----------------------------- | ---------------------------------------------------- | | `{module}.test.ts` | Core CRUD behavior, happy paths, edge cases | | `{module}.types.test.ts` | Type inference verification, negative type tests | | `{module}.{scenario}.test.ts` | Specific scenarios (CRDT sync, offline, integration) | ### When to Split - File exceeds ~500 lines - Tests cover genuinely distinct concerns (CRUD vs sync vs types) - Different setup requirements per concern ### When NOT to Split - Splitting would create files with fewer than 3 tests - All tests share the same setup and concern ## Test Naming Test descriptions MUST be behavior assertions, not vague descriptions. The name should tell you what broke when the test fails. ### Rules 1. **State what happens**, not "should work" or "handles correctly" 2. **Include the condition** when testing edge cases 3. **No filler words**: "should", "correctly", "properly" add nothing ### Good Names ```typescript test('upsert stores row and get retrieves it', () => { ... }); test('filter returns only published posts', () => { ... }); test('concurrent edits to different fields: both preserved', () => { ... }); test('delete vs update race: update wins (rightmost entry)', () => { ... }); test('observer fires once per transaction, not per operation', () => { ... }); test('get() throws for undefined tables with helpful message', () => { ... }); ``` ### Bad Names ```typescript test('should work correctly', () => { ... }); // What works? What's correct? test('should handle batch operations', () => { ... }); // Handle how? test('basic test', () => { ... }); // Says nothing test('should create and retrieve rows correctly', () => { ... }); // Vague "correctly" ``` ### Pattern: `{action} {outcome} [condition]` ``` "upsert stores row and get retrieves it" ^^^^^^ ^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^ action outcome action outcome "observer fires once per transaction, not per operation" ^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ subject outcome condition "get() returns not_found for non-existent rows" ^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^ action outcome condition ```
在 GitHub 查看