vitest-best-practices
Installation
SKILL.md
Vitest Best Practices
Comprehensive patterns for writing maintainable, effective vitest tests. Focused on expert-level guidance for test organization, clarity, and performance.
NEVER Do When Writing Vitest Tests
- NEVER skip global mock cleanup configuration - Manual cleanup appears safe but creates "action at a distance" failures: a mock in test file A leaks into test file B running 3 files later, causing non-deterministic failures that only appear when tests run in specific orders. These Heisenbugs waste hours in CI debugging. Configure
clearMocks: true,resetMocks: true,restoreMocks: trueinvitest.config.tsonce to eliminate this entire class of order-dependent failure. - NEVER nest describe blocks more than 2 levels deep - Deep nesting creates cognitive overhead and excessive indentation. Put context in test names instead:
it('should add item to empty cart')vsdescribe('when cart is empty', () => describe('addItem', ...)). - NEVER mock your own pure functions - Mocking internal code makes tests brittle and less valuable. Mock only external dependencies (APIs, databases, third-party libraries). Prefer fakes > stubs > spies > mocks.
- NEVER use loose assertions like
toBeTruthy()ortoBeDefined()- These assertions pass for multiple distinct values you never intended:toBeTruthy()passes for1,"false",[], and{}- all semantically different. When refactoring changesgetUser()from returning{id: 1}to returning1, your test still passes but your production code breaks. Loose assertions create false confidence that evaporates in production.toBeTypeOf()is NOT a loose assertion. - NEVER test implementation details instead of behavior - Tests that verify "function X was called 3 times" create false failures: you optimize code to call X once via memoization, all tests fail, yet the user experience is identical (and faster). These tests actively punish performance improvements and refactoring. Test what users observe (outputs given inputs), not how your code achieves it internally.
- NEVER share mutable state between tests - Tests that depend on execution order or previous test state create flaky, unreliable suites. Each test must be fully independent with fresh setup.
- NEVER use
anyor skip type checking in test files - When implementation signatures change, tests withas anysilently pass while calling functions with wrong arguments. You ship broken code that TypeScript could have caught. Tests are executable documentation:user as anycommunicates nothing, butcreateTestUser(Partial<User>)shows exactly what properties matter for this test case.
Before Writing Tests, Ask
Apply these expert thinking patterns before implementing tests: