The Philosophy of Testing
In modern software engineering, automated testing is not a luxury but a requirement. It ensures that changes don’t break existing functionality (regressions) and serves as executable documentation. We follow the Testing Pyramid model: a broad base of fast Unit Tests, a middle layer of Integration Tests, and a thin top layer of End-to-End (E2E) tests.
Unit Testing with Jest
Jest is a delightful JavaScript Testing Framework with a focus on simplicity. It provides an assertion library, test runner, and mocking support out of the box.
The Anatomy of a Test
Tests are typically organized into describe blocks (grouping related tests) and it or test blocks (individual test cases).
describe('Math Utilities', () => {
test('adds 1 + 2 to equal 3', () => {
expect(add(1, 2)).toBe(3);
});
});
Mocking and Dependency Injection
In isolation, a unit test should not depend on external systems like databases or web APIs. We use Mocks and Spies to simulate these dependencies.
jest.fn(): Creates a mock function that tracks its calls.jest.mock('module-name'): Replaces an entire module with mocked versions of its exports.jest.spyOn(object, 'method'): Watches a real method but allows you to track its usage or override its implementation.
const api = require('./api');
jest.mock('./api');
test('fetches data on click', async () => {
api.getData.mockResolvedValue({ status: 200 });
await triggerLoad();
expect(api.getData).toHaveBeenCalledTimes(1);
});
React Component Testing
Testing UI components requires a different approach. Instead of testing internal implementation details (like state variables), we test observable behavior from the user’s perspective. The React Testing Library (RTL) is the industry standard for this.
RTL encourages you to find elements by their role or text content (e.g., getByRole('button', { name: /submit/i })), mimicking how a screen reader or a real user would find them.
Test-Driven Development (TDD)
TDD is a workflow where you write the test before the implementation:
- Red: Write a failing test for a small piece of functionality.
- Green: Write the minimum code necessary to make the test pass.
- Refactor: Improve the code while ensuring the tests stay green.
Why is it generally discouraged to test internal component state in React?
Continuous Integration (CI)
In a professional environment, tests are run automatically on every “Push” or “Pull Request” using CI tools like GitHub Actions. This ensures that only code that passes all verification steps can be merged into the main codebase.