Configuration and Speed
Configuration and Speed
Vitest reuses your Vite config, so aliases, plugins, and TypeScript transform settings apply to tests automatically:
// vitest.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
environment: "node", // or "jsdom" / "happy-dom"
globals: true,
restoreMocks: true,
},
});
Tests run as native ESM in worker threads — no separate Babel/ts-jest pipeline — which is why watch mode and startup are fast. Vite resolve.alias covers path mappings for both app and tests, so tsconfig paths and test resolution cannot drift apart.
Mocking with vi
Mocking with vi
vi.mock is hoisted like Jest's, and its factory can reference vi.hoisted for values that must exist first:
const ids = vi.hoisted(() => ({ next: 1 }));
vi.mock("../src/db", () => ({
insert: vi.fn(() => ({ id: ids.next++ })),
}));
import { insert } from "../src/db";
expect(insert({ a: 1 })).toEqual({ id: 1 });
Timers: vi.useFakeTimers() then vi.advanceTimersByTime(1000) drives debounce and interval logic without real waiting. vi.spyOn(obj, "method") keeps original behavior until you mockReturnValue, and expect(...).toHaveBeenCalled works because Vitest's assertion layer shares Jest-compatible matchers.
Pitfalls and Interview Angles
Pitfalls and Interview Angles
Specifier sensitivity. vi.mock("../src/db") must match how the test imports the module — same relative path, or mocking silently applies to a different resolved file.
Globals on versus off. With globals: false (the default) you import describe/it/expect from vitest; editor types then come from vitest/globals only when globals are enabled.
Coverage and type testing. coverage: { provider: "v8" } instruments without instrumenting transforms, and expectTypeOf<T>().toMatchTypeOf<U>() (or assertType) checks generics at compile time in the same file.
Interview: contrast Vitest with Jest architecturally — Vite's module graph and native ESM versus Jest's CommonJS-centric registry — and say which config keys must move when migrating.
Quiz
1. Why do Vitest tests pick up your path aliases without extra mapping?
2. What does vi.hoisted solve?
3. Which pair correctly drives fake time?
Flashcards
Question
Vitest core advantage
Click to reveal answer
Answer
Runs through Vite: native ESM, shared config/plugins/aliases, instant watch startup.
Question
vi.mock
Click to reveal answer
Answer
Hoisted module mock keyed by import specifier — must match the test's import path.
Question
vi.hoisted
Click to reveal answer
Answer
Declares values that a hoisted vi.mock factory can safely reference.
Question
Fake timers
Click to reveal answer
Answer
vi.useFakeTimers() + vi.advanceTimersByTime(ms) to test debounce/intervals without waiting.
Question
expectTypeOf
Click to reveal answer
Answer
Compile-time assertion helper that fails the build when generic types mismatch.
Revision Notes
Key Takeaways
- 1. One Vite config serves app build and tests, so aliases cannot drift
- 2. Mock paths must mirror import specifiers exactly
- 3. Type assertions run in the same suite as runtime tests
Interview Tips
- • Explain the architectural difference from Jest: Vite module graph vs Jest registry
- • Walk a fake-timer test for a debounce function step by step
Cheat Sheet
Vitest reuses vite.config aliases; vi.mock hoisted (match specifier), vi.hoisted for shared vars; useFakeTimers + advanceTimersByTime; expectTypeOf for type tests.