Skip to main content

typescript-test-writing

Use this skill when writing or modifying tests in the llxprt-code repository. Covers mandatory TDD, behavioral testing, bun:test conventions and file naming, mock hygiene (no mock theater), and what never to test. Distilled from dev-docs/RULES.md, which remains the source of truth.

Quellinformationen

Repository
vybestack/llxprt-code
Letzte Quellaktivität
18. August 2026 um 14:13
Erkannte Sprache von SKILL.md
Englisch
Sterne
703
Forks
96

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
typescript-test-writing
description
Use this skill when writing or modifying tests in the llxprt-code repository. Covers mandatory TDD, behavioral testing, bun:test conventions and file naming, mock hygiene (no mock theater), and what never to test. Distilled from dev-docs/RULES.md, which remains the source of truth.
# Writing TypeScript Tests in llxprt-code Distilled from dev-docs/RULES.md. When in doubt, read RULES.md in full — it is the source of truth for development guidelines. ## Core principle: TDD is mandatory Every line of production code must be written in response to a failing test. No exceptions. Red-Green-Refactor, followed strictly: 1. **RED**: Write a failing test for the next small behavior. 2. **GREEN**: Write ONLY enough code to make the test pass. 3. **REFACTOR**: Only if it improves clarity. 4. **COMMIT**: Feature + tests together; refactoring separately. ## Stack rules (non-negotiable) - Bun + `bun:test` ONLY. Never create Vitest or Node test suites, and never add new `.js` files — everything is TypeScript run with bun. - TypeScript strict mode: no `any` (use `unknown` with type guards), no type assertions (use type predicates), explicit return types. - Import pattern (bun:test re-exports `vi`, `mock`, and `Mock`): ```typescript import { describe, it, expect, beforeEach, afterEach, vi, type Mock, } from 'bun:test'; ``` - Prefer immutable data in tests and fixtures (`{ ...cart, items: [...cart.items, item] }`); never mutate shared fixtures between tests. ## Test behavior, not implementation ✅ Test: - Public API behavior - Input → Output transformations - Edge cases and error conditions - Integration between units - Schema validation ❌ Never test: - Implementation details - Private methods - Third-party libraries - Mock interactions ❌ Never enshrine bugs as specification: do not write a passing test that asserts incorrect behavior, even if that is what the code currently does. If you discover a bug while writing tests: (1) file an issue or ask the user, (2) write a failing test that asserts the CORRECT behavior, (3) fix the production code so the test passes. A suite that encodes bugs as passing tests is worse than no tests — it actively prevents future fixes. ## Test structure - **describe**: feature/component name. - **it**: specific behavior in plain English. - **Arrange-Act-Assert**: clear sections; one behavior per test. - **DRY setup**: never copy-paste identical beforeEach/afterEach boilerplate (e.g. temp-dir creation, cleanup) across describe blocks. Extract a shared helper that wires the lifecycle hooks (e.g. a `useTempDir()` helper that registers beforeEach/afterEach internally and returns a lazy accessor). Repeating 5+ lines of identical setup in N describe blocks is a maintenance hazard; one line of shared setup per describe block is the target. ## File conventions - Tests are TypeScript, co-located with the code under test. - `*.test.ts` is the dominant pattern in this repo; `*.spec.ts` and `__tests__/` directories are also in use — match the existing pattern of the neighboring tests before creating a new file. - File names: kebab-case. - Run a single file with `bun test path/to/file.test.ts`; the full suite is `npm run test`. ## Mock hygiene: no mock theater **The fundamental rule: you cannot test a component by mocking that component.** ### Mock decision tree ``` Is it the component you're testing? ├─ Yes → [ERROR] NEVER MOCK IT └─ No → Is it doing the core work being tested? ├─ Yes → [ERROR] DON'T MOCK IT └─ No → Is it infrastructure (FS, network, DB)? ├─ Yes → [OK] OK to mock └─ No → Is it completely unrelated to the test? ├─ Yes → [OK] OK to mock └─ No → WARNING: Probably shouldn't mock ``` ### Prohibited mock patterns 1. **Self-mocking** — mocking the component under test. If you mock EmojiFilter to test EmojiFilter, you are testing the mock, not the component. 2. **Direct-value mock** — a mock that returns exactly the output the test expects. The test is worthless: it can never fail for the right reason. 3. **Mock verification** — asserting a mock was called (`expect(mockFn).toHaveBeenCalledWith(...)`) proves nothing about real code. 4. **Mirror-echo assertion** — configuring a stub with a literal and asserting that same literal back (`.mockReturnValue('JIT_MARKER')` … `expect(policy.marker).toBe('JIT_MARKER')`). Unless producing that literal IS the behavior under test, the test asserts its own configuration and can never fail for the right reason. Assert a derived property instead — ordering, precedence, counts, aggregation — with literals on the input side only. Also watch for: the mock chain (A calls MockB calls MockC — no real code tested) and mocks with complex implementations (if the mock has the logic, test the real code instead). ### Allowed mock patterns 1. **Infrastructure mocking** — mock filesystem, network, databases (not business logic); instantiate the REAL component; assert real transformations through the real code paths. 2. **Irrelevant service mocking** — mock services unrelated to the behavior under test (auth, logging), while the component under test is real. 3. **Test data builders** — build input data; never mock behavior. ### The litmus test After writing a test, all four answers must be the "good" one: 1. If I delete the real implementation, will this test fail? (If NO: worthless) 2. If I break the real implementation, will this test catch it? (If NO: worthless) 3. Am I testing my mock or my code? (If MOCK: worthless) 4. Could I replace the component with `return 'expected'` and pass? (If YES: worthless) ## Before submitting - All tests pass (`npm run test`). - No TypeScript errors (`npm run typecheck`). - No linting warnings (`npm run lint`). - No console.logs or debug code left behind. - Every production line you touched is covered by behavior tests. - Self-check files you touched with the AST test-audit scanner: `bun scripts/test-audit/scan.ts` — no new MOCK_MIRROR, ALWAYS_TRUE, SELF_CONFIRMING, or NO_ASSERT findings may appear on them. The scanner runs over the full corpus and writes `findings.tsv` + `file-stats.tsv` to the output dir (defaults to `tmp/test-audit`). To compare against the baseline, run it once on `main` with a custom output dir, then on your branch with a different output dir, and diff the TSVs: `bun scripts/test-audit/scan.ts tmp/scan-main` vs `bun scripts/test-audit/scan.ts tmp/scan-branch`, then `diff tmp/scan-main/findings.tsv tmp/scan-branch/findings.tsv`. Note: the scanner compares exact stable literals (not substrings) between mock configurations and assertion arguments. A MOCK_MIRROR finding on a test that verifies a transformation (e.g., asserting `'A B'` where the stub returns `'A'` and `'B'` separately) is a false positive — the assertion literal does not exactly match any single mock literal. Inspect the exact positive equality that triggered the finding before dismissing it. Disjoint-source and leakage assertions (e.g., `not.toContain('X')`) are not mirror echoes and will not trigger MOCK_MIRROR.
Auf GitHub ansehen