Concept
Writing Assertions
A unit test describes a single requirement, executing code and verifying results against expected constraints using assertions:
import { describe, test, expect } from 'vitest';
import { calculateTotal } from './cart';
describe('calculateTotal()', () => {
test('should sum items and apply discount', () => {
const items = [
{ price: 100, quantity: 2 },
{ price: 50, quantity: 1 }
];
const total = calculateTotal(items, 10); // 10% discount
expect(total).toBe(225); // (200 + 50) - 25
});
test('should return 0 for empty arrays', () => {
expect(calculateTotal([], 0)).toBe(0);
});
});Hooks: Setup & Teardown
Hooks execute logic at specific intervals inside the test runner process, useful for resetting databases or clearing timers:
import { beforeEach, afterEach } from 'vitest';
beforeEach(() => {
// Runs once before EACH test block
initializeTestDatabase();
});
afterEach(() => {
// Runs once after EACH test block
clearDatabase();
});Jest vs Vitest: The Shift to ESM
- Jest: The historical industry standard. Built on CommonJS, requiring heavy transpilers (like Babel) to support modern ES Modules, causing setup frictions in TypeScript.
- Vitest: The modern successor. Built natively on Vite, supporting ESM, TypeScript, and JSX out of the box with zero configuration. It reuses your Vite configuration file, making it much faster (powered by esbuild) and easier to integrate.
Common Mistakes
1. Sharing mutable state variables across test blocks
Declaring a global let variable and mutating it inside test blocks can result in tests bleeding into each other:
// ❌ WRONG: Modifying shared state
let cart = [];
test('add item', () => { cart.push('item'); ... });
test('should be empty', () => { expect(cart.length).toBe(0); }); // Fails!Always redeclare states locally inside beforeEach() or helper factory functions.
2. Nesting describe blocks too deeply
Nesting describe blocks 5+ levels deep makes test output files difficult to read and maintain. Keep suites flat, grouping tests logically by function names.
Best Practices
- AAA Pattern: Structure tests clearly: Arrange (set up variables), Act (execute the function), Assert (verify result).
- Verify edge cases: Do not just test happy paths. Assert how your code behaves with empty inputs, nulls, negative numbers, or invalid formats.
- Ensure isolation: A single test block should never depend on the execution of a prior test block.
