Testing Standards
Overview
Testing patterns and conventions using Vitest. Tests live alongside source files with the .test.ts extension. These rules cover file organization, mocking strategies, and coverage expectations.
Rules
File Structure and Naming
Place test files next to the source they test. Use .test.ts as the extension. Name describe blocks after the module or function under test, and write test cases as "should + expected behavior."
Correct
Write Clear Test Cases
Each test should have a single assertion focus. Use async/await for asynchronous code and toMatchObject for partial matching.
Correct
Mock External Dependencies
Use vi.mock for module-level mocks and vi.fn for individual functions. Replace real I/O (file system, network) with deterministic mocks.
Correct
Exception: per-test module factories
Use vi.doMock when the code under test loads a module through a dynamic
await import() and individual tests need different factories for the same
specifier. vi.mock hoists to the top of the file and applies one factory to
every test, so those tests would share the first fixture registered.
autoload() in packages/maltty is the case this covers -- each test declares a
different fake command module for the same path.
vi.clearAllMocks() and vi.restoreAllMocks() reset call history and spies but
leave the registered factory in place, and vi.resetModules() only drops the
module cache. To retire a factory, call vi.doUnmock(specifier) for each mocked
path and then vi.resetModules() before the next dynamic import -- otherwise a
later test reuses the previous test's module.
Organize Tests by Feature
Group related tests with nested describe blocks. Use beforeEach to reset mocks and shared state before each test.
Correct
Meet Coverage Requirements
Target the following minimum coverage levels by area.
Test Edge Cases and Error Handling
Test pure functions exhaustively, including boundary values and error paths.