Good tests catch bugs early and enable confident refactoring. This guide covers unit testing patterns and best practices.
Test Structure (AAA Pattern)#
1describe('calculateTotal', () => {
2 it('should apply percentage discount correctly', () => {
3 // Arrange
4 const items = [{ price: 100, quantity: 2 }];
5 const discount = { type: 'percentage', value: 10 };
6
7 // Act
8 const result = calculateTotal(items, discount);
9
10 // Assert
11 expect(result).toBe(180);
12 });
13});Descriptive Test Names#
1// ✅ Good: Describes behavior
2it('should return empty array when input is empty', () => {});
3it('should throw ValidationError when email is invalid', () => {});
4
5// Use test.each for multiple cases
6test.each([
7 { input: '', expected: true },
8 { input: 'hello', expected: false },
9])('isEmpty("$input") should return $expected', ({ input, expected }) => {
10 expect(isEmpty(input)).toBe(expected);
11});Mocking#
1import { vi } from 'vitest';
2
3vi.mock('./api', () => ({
4 fetchUser: vi.fn(),
5}));
6
7describe('UserService', () => {
8 beforeEach(() => vi.clearAllMocks());
9
10 it('should fetch user data', async () => {
11 vi.mocked(fetchUser).mockResolvedValue({ id: '1', name: 'John' });
12
13 const result = await userService.getUser('1');
14 expect(result.name).toBe('John');
15 });
16});Testing Async Code#
1it('should handle async operations', async () => {
2 await expect(fetchData()).resolves.toHaveProperty('items');
3});
4
5it('should reject on error', async () => {
6 await expect(fetchInvalid()).rejects.toThrow('Not found');
7});Write tests that describe behavior, keep them isolated and fast, and aim for meaningful coverage.