Testing: Building Confidence in Your Code
DraftURL: https://ruvebal.github.io/web-atelier-udit/lessons/en/react/react-testing/
π Table of Contents
- π― Sprint Goal
- π Position in Journey
- π§ Learning Objectives
- ποΈ The Testing Trophy
- π§ Testing Stack
- π Methodology: Atelier Practice
- π Sprint Deliverables
- π‘ Test Examples
- π Lesson Navigation
- π Key Concepts Preview
βTests are not about finding bugs. Tests are about building confidence to change.β
π― Sprint Goal
By the end of this sprint: Your app has a testing foundationβunit tests for logic, component tests for UI behavior, and at least one end-to-end test for a critical user flow.
π Position in Journey
| Sprint | Focus | Your App Grows |
|---|---|---|
| 9. Backend | Data fetching | Real data, real app |
| 10. Auth | Security | User sessions |
| β 11. Testing | Quality | Reliable codebase |
| 12. Performance | Speed | Optimized experience |
π§ Learning Objectives
By the end of this lesson, you will:
- Write unit tests for pure functions and hooks
- Test React components with React Testing Library
- Mock API calls in component tests
- Write at least one Cypress E2E test
- Understand the Testing Trophy (what to test most)
- Set up CI to run tests on every push
ποΈ The Testing Trophy
βββββββββ
β E2E β β Few, critical paths
βββββ¬ββββ
ββββββ΄βββββ
βIntegrationβ β Most of your tests
ββββββ¬ββββββ
ββββββββ΄βββββββ
β Unit β β Pure functions, hooks
ββββββββ¬βββββββ
Focus: Does the user's goal get accomplished?
Avoid: Testing implementation details
π§ Testing Stack
| Type | Tool | Tests What |
|---|---|---|
| Unit | Vitest | Pure functions, utilities |
| Component | React Testing Library | User interactions with UI |
| Integration | RTL + MSW | Components with mocked APIs |
| E2E | Cypress | Full app flows in browser |
π Methodology: Atelier Practice
The Sprint Rhythm
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DAY 1: Unit & Component Tests β
β β’ Set up Vitest and React Testing Library β
β β’ Write unit tests for utility functions β
β β’ Test a form component (render, type, submit) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β DAY 2: Integration & Mocking β
β β’ Set up MSW (Mock Service Worker) for API mocking β
β β’ Test a data-fetching component end-to-end β
β β’ Test error and loading states β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β DAY 3: E2E & CI β
β β’ Write one Cypress test for login β dashboard flow β
β β’ Set up GitHub Actions to run tests on push β
β β’ Celebrate green checkmarks β
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
What to Test (Priority Order)
| Priority | Test This | Example |
|---|---|---|
| π΄ High | User can complete critical flow | Login, checkout, create post |
| π Medium | Component handles states | Loading, error, empty, success |
| π‘ Lower | Edge cases | Very long text, special characters |
| βͺ Skip | Implementation details | Internal state shape, CSS classes |
AI-Assisted Development Protocol
| Task | AI Role | Your Role |
|---|---|---|
| Generate test cases | Scaffold test structure | Add assertions that matter |
| Mock complex APIs | Create MSW handlers | Verify they match real API |
| Debug failing tests | Explain the error | Understand why it fails |
| Increase coverage | Suggest edge cases | Prioritize important paths |
π Sprint Deliverables
- 5+ unit tests for utilities and pure functions
- 3+ component tests using RTL
- MSW setup for mocking API in tests
- 1 Cypress E2E test for critical flow
- GitHub Actions workflow running tests on push
- Test coverage report (aim for 60%+ on core code)
- Reflection: What did tests reveal about your code?
π‘ Test Examples
Unit Test (Vitest)
// src/utils/formatPrice.test.js
import { formatPrice } from './formatPrice';
describe('formatPrice', () => {
it('formats whole numbers', () => {
expect(formatPrice(1000)).toBe('$10.00');
});
it('handles zero', () => {
expect(formatPrice(0)).toBe('$0.00');
});
});
Component Test (RTL)
// src/components/LoginForm.test.jsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { LoginForm } from './LoginForm';
test('submits email and password', async () => {
const handleSubmit = vi.fn();
render(<LoginForm onSubmit={handleSubmit} />);
await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com');
await userEvent.type(screen.getByLabelText(/password/i), 'password123');
await userEvent.click(screen.getByRole('button', { name: /login/i }));
expect(handleSubmit).toHaveBeenCalledWith({
email: 'test@example.com',
password: 'password123'
});
});
π Lesson Navigation
| Previous | Current | Next |
|---|---|---|
| Authentication | Testing | Performance |
π Key Concepts Preview
What βgood testingβ means (in this course)
- Tests are a change-enabler: the goal is confidence to refactor, not β100% coverageβ.
- Prefer integration tests for user-visible behavior (forms, flows, navigation).
- Unit test pure logic (reducers, validators, formatters).
- Avoid brittle tests (testing implementation details, internal state, DOM structure).
Minimal stack (recommended)
- Unit / component: Vitest + React Testing Library
- Network mocking: MSW (Mock Service Worker)
- E2E smoke (optional): Cypress (or Playwright if you already know it)
Example: test behavior, not structure
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
it('submits the form with user input', async () => {
const user = userEvent.setup();
const onSubmit = vi.fn();
render(<LoginForm onSubmit={onSubmit} />);
await user.type(screen.getByLabelText(/email/i), 'test@example.com');
await user.type(screen.getByLabelText(/password/i), 'password123');
await user.click(screen.getByRole('button', { name: /login/i }));
expect(onSubmit).toHaveBeenCalledWith({ email: 'test@example.com', password: 'password123' });
});
Reflection (Atelier)
π Which bug did your tests preventβspecifically? What changed in your code because tests existed?
π Which test became too hard to write? What does that reveal about your architecture?
Koan
βIf your tests require lies, your design is already lying.β
βWrite tests. Not too many. Mostly integration.β β Guillermo Rauch