Testing vitest
Skill Syo-M/fable-frontend-skills/plugin/skills/testing-vitest
Measured, opinionated Claude Code rules for frontend work (React / Next.js / Vite / Astro) — skills, path rules, review agents, sign-off hooks, installer & plugin. Every claim backed by committed eval reports.
npx -y skills add Syo-M/fable-frontend-skills --skill testing-vitestAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Vitest unit and component testing conventions — Testing Library, mocking policy, MSW, timers. Use when writing or fixing unit/component tests or vitest config. 日本語の依頼例:「ユニットテスト書いて」「Vitest」「テスト追加して」「モック」「ロジックのテスト」。
SKILL.md
4.0 KB, as published. Nobody here has run it
Vitest
What to test, where
- Pure logic (utils, reducers, schema transforms) → plain unit tests. Fast, no DOM.
- Component behavior → Storybook play functions are the primary component-test layer (see
storybookskill); write direct Testing Library tests only for headless hooks/providers with no visual states, or when exhaustively covering a pure prop/branch matrix (roughly ≥ 6 combinations) where one story per case would bloat the catalog — the meaningful visual states still get stories. - Full user flows → Playwright (see
testing-playwright). Don't simulate routing/auth flows in jsdom.
Worked examples (the layer decision people get wrong most):
- "Test that the Button shows a spinner while submitting" → Storybook play function — a visible state + interaction the catalog should own; not a Vitest component test.
- "Test that
formatCurrencyrounds half-up and handles -0" → Vitest unit test — pure logic, no DOM, no story. - "Test the
useDebouncehook's timing" → Vitest unit test with fake timers — headless hook, no visual state. - "Test the checkout form across 8 field-validation combinations" → Vitest for the exhaustive matrix, plus one story per meaningful visual state (empty / error / submitting) — don't make 8 stories.
- Test behavior users observe, not implementation: no asserting on state internals, no
container.querySelector('.styles_button_x'), no spying on internal functions of the unit under test.
Structure
- Colocate:
format.ts+format.test.ts. Name tests as behavior sentences:it('disables submit while the request is in flight'). - Arrange–Act–Assert, one behavior per test. Shared setup in a plain
setup()factory function returning what tests need — avoid sprawlingbeforeEachmutation. - No logic in tests (if/loops computing expectations). Expected values are literals.
Testing Library rules
- Query priority (same list as
testing-playwright):getByRole(withname) >getByLabelText>getByText>getByTestId(last resort, added deliberately). Never query by placeholder — a placeholder is not a label (seea11y); needing it means the input lacks one. - All interactions via
userEvent(const user = userEvent.setup()), neverfireEvent— userEvent fires the full real event sequence. - Async UI:
await screen.findByRole(...)/waitForfor assertions only — neverwaitForcontaining a user action, never arbitrarysetTimeoutsleeps. - Wrap components needing providers with a shared
renderWithProviderstest util — one definition, not per-file copies.
Mocking policy — mock at the boundary, not the module graph
- Network: MSW handlers, not
vi.mock-ing your own fetch wrapper. Tests then survive refactors of the data layer. vi.mockonly for true externals (analytics SDK, payment widget) and module-level non-determinism. If you're mocking your own module to make a test pass, the design or the test level is wrong.- Non-determinism:
vi.useFakeTimers()+vi.setSystemTime()for time; alwaysvi.useRealTimers()inafterEach. With fake timers + userEvent, configureadvanceTimers: vi.advanceTimersByTime. vi.restoreAllMocks()inafterEach(orrestoreMocks: truein config) — leaked mocks cause order-dependent flake.
Config
- Single source: define
testinvite.config.ts(or merge it) so aliases/plugins match the app.environment: 'jsdom'only for DOM test projects; pure-logic tests run innode(faster) — use Vitest projects to split. - Coverage is a signal, not a goal: review uncovered branches, don't chase a number with assertion-free tests.
- A test that fails intermittently is a bug to fix now (usually unawaited async or shared state) — never retry-loop or skip it.