writing-tests

Write Shift tests that assert on user-observable state instead of mock invocation details.

217|7|Updated Dec 9, 2024
One-click install
npx skills add https://github.com/shift-editor/shift --skill writing-tests-shift-editor
Or copy as Structured Prompt for Agent
Please help me install this Agent Skill.
Skill: writing-tests
Source: https://github.com/shift-editor/shift/tree/main/.claude/skills/writing-tests
Command: npx skills add https://github.com/shift-editor/shift --skill writing-tests-shift-editor

SYSTEM DOCUMENTATION & REQUIREMENTS

What problem does it solve?

writing-tests helps developers avoid brittle, mock-heavy unit tests that break during refactors without actually catching real user-visible regressions.

Core Features & Use Cases

  • Assert on observable behavior, not mock internals: Drive tests through user-facing surfaces like pointer/keyboard actions and command history, then assert on the resulting state a user would notice.
  • Prevent mock-based drift: Replace “what was called” assertions with checks on glyph contours, selection, viewport pan, tool state, return values, and emitted payloads.
  • Use the right test category: Choose Tool tests, Command tests, Pure module tests, or Bridge tests so the test’s harness matches the behavior’s boundaries.

Quick Start

Use the writing-tests skill when you are about to add, rewrite, or review a .test.ts file, especially if you’re tempted to vi.fn, vi.spyOn, or global stubs to make the test pass.

Frequently Asked Questions about writing-tests

High-intent search queries and answers about installing and using this skill.

FAQPage Schema
How do I write refactor-proof tests that don't break when mocking internals?

Refactor-proof tests assert on user-observable state like selection or return values rather than mock invocation details. You drive tests through user-facing surfaces and check the resulting state a user would notice, preventing mock-based drift during code changes.

Why does my test break during refactors even though the output is correct?

Tests break during refactors when they assert on mock internals or what was called instead of actual behavior. By verifying observable state such as glyph contours, viewport pan, or emitted payloads, tests remain stable across implementation changes while catching real regressions.

When should I use bridge tests instead of pure module tests?

Bridge tests validate interactions with the real Editor or Rust engine boundaries, while pure module tests verify isolated logic. Choosing the right test category ensures the harness matches the behavior’s boundaries, driving through user-facing surfaces like TestEditor and NativeBridge.

Can I use global stubs and vi.fn to make tool behavior tests pass?

Global stubs and vi.fn calls should be avoided in tool behavior tests. Instead of asserting on mock invocations, you should drive tests through pointer and keyboard actions, command history, and then assert on the resulting tool state or emitted payloads.

What's the best way to test command undo and redo contracts?

Testing command undo and redo contracts requires driving actions through command history and asserting on user-observable state. This approach avoids brittle mock-heavy assertions, ensuring the test verifies the actual contract behavior rather than internal mock call sequences.

Does this testing approach work with native bridge validation?

Native bridge validation works by interacting with real Editor or Rust engine boundaries. You drive tests through the NativeBridge surface and assert on observable state or emitted payloads, avoiding global stubs and parallel-world harnesses for accurate boundary verification.