Testing JavaScript Applications
Testing gives you confidence in your code. Here's how to do it right.
Why Test?
- Catch bugs early - Fix issues before production
- Enable refactoring - Change code safely
- Document behavior - Tests show how code works
- Improve design - Testable code is better code
Unit Testing with Jest
Test individual functions:
// math.js
export function add(a, b) {
return a + b;
}
// math.test.js
import { add } from './math';
describe('add', () => {
it('adds two positive numbers', () => {
expect(add(2, 3)).toBe(5);
});
it('handles negative numbers', () => {
expect(add(-1, 5)).toBe(4);
});
});
Testing React Components
Use React Testing Library:
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';
describe('Counter', () => {
it('renders initial count', () => {
render(<Counter initialCount={0} />);
expect(screen.getByText('Count: 0')).toBeInTheDocument();
});
it('increments when clicked', () => {
render(<Counter initialCount={0} />);
fireEvent.click(screen.getByRole('button'));
expect(screen.getByText('Count: 1')).toBeInTheDocument();
});
});
Mocking
Isolate units by mocking dependencies:
// Mock a module
jest.mock('./api');
import { fetchUser } from './api';
fetchUser.mockResolvedValue({ name: 'John' });
// Mock a function
const mockCallback = jest.fn();
mockCallback.mockReturnValue(42);
Async Testing
Test asynchronous code:
it('fetches user data', async () => {
const user = await fetchUser(1);
expect(user.name).toBe('John');
});
// With Testing Library
it('displays loaded data', async () => {
render(<UserProfile userId={1} />);
expect(await screen.findByText('John')).toBeInTheDocument();
});
Test Coverage
Aim for meaningful coverage:
// jest.config.js
{
"collectCoverage": true,
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80
}
}
}
Integration Testing
Test how parts work together:
describe('Checkout flow', () => {
it('completes purchase', async () => {
render(<App />);
// Add item to cart
fireEvent.click(screen.getByText('Add to Cart'));
// Go to checkout
fireEvent.click(screen.getByText('Checkout'));
// Fill form and submit
fireEvent.change(screen.getByLabelText('Email'), {
target: { value: 'test@example.com' }
});
fireEvent.click(screen.getByText('Place Order'));
// Verify success
expect(await screen.findByText('Order Confirmed')).toBeInTheDocument();
});
});
Conclusion
Start with unit tests, add integration tests for critical flows, and maintain good coverage. Your future self will thank you.