Testing vitest
Skill Syo-M/fable-frontend-skills/.claude/skills/testing-vitest
Vitest unit and component testing conventions — Testing Library, mocking policy, MSW, timers. Use when writing or fixing unit/component tests or vitest config. 日本語の依頼例:「ユニットテスト書いて」「Vitest」「テスト追加して」「モック」「ロジックのテスト」。From its SKILL.md
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.
SKILL.md
4.0 KB, 882 tokens by cl100k_base, 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.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.
Gives 3 of the 12 instructions most unit integration skills give in 882 tokens
Counted across 149 of the 202 authors here whose files we hold, read 2026-09-06
- Structure tests as arrange, act, assertin 17 of 149
- Test behavior, not implementationhere, and in 16 of 149
- Mock all external dependenciesin 14 of 149
- Keep tests independentin 9 of 149
- Implement minimal code to passin 7 of 149
- Write a failing test firstin 7 of 149
- Fix or delete flaky tests immediatelyhere, and in 7 of 149
- Verify one behaviour per test functionhere, and in 7 of 149
- Name tests MethodName_StateUnderTest_ExpectedBehaviorin 6 of 149
- Separate arrange, act, and assert with blank linesin 6 of 149
- Target at least 80 percent core logic coveragein 6 of 149
- Create factory fixtures for test datain 6 of 149
Said here and by no other author read
- Prefer getByRole queries, test-id last
- Await findBy or waitFor for async assertions
- Wrap provider components in shared renderWithProviders
- Mock network with MSW handlers
- Restrict vi.mock to true externals
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.