← Về danh sách bài họcBài 23/25
🧪 Bài 23: Testing - Kiểm Thử Components
🎯 Sau bài học này, bạn sẽ:
- Cài đặt Vitest + React Testing Library
- Viết unit test cho components
- Test user interactions và async operations
- Test custom hooks
1. Setup Testing
npm install -D vitest @testing-library/react @testing-library/jest-dom @testing-library/user-event jsdom// vite.config.js
export default defineConfig({
test: {
environment: 'jsdom',
setupFiles: './src/test/setup.js',
globals: true,
}
});
// src/test/setup.js
import '@testing-library/jest-dom';2. Component Testing
// Counter.jsx
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p data-testid="count">Count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
// Counter.test.jsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
describe('Counter', () => {
it('hiển thị giá trị ban đầu', () => {
render(<Counter />);
expect(screen.getByTestId('count')).toHaveTextContent('Count: 0');
});
it('tăng count khi click', async () => {
const user = userEvent.setup();
render(<Counter />);
await user.click(screen.getByText('Increment'));
expect(screen.getByTestId('count')).toHaveTextContent('Count: 1');
await user.click(screen.getByText('Increment'));
expect(screen.getByTestId('count')).toHaveTextContent('Count: 2');
});
it('reset về 0', async () => {
const user = userEvent.setup();
render(<Counter />);
await user.click(screen.getByText('Increment'));
await user.click(screen.getByText('Reset'));
expect(screen.getByTestId('count')).toHaveTextContent('Count: 0');
});
});3. Testing Async & API Calls
import { render, screen, waitFor } from '@testing-library/react';
// Mock fetch
globalThis.fetch = vi.fn();
describe('UserList', () => {
it('hiển thị users từ API', async () => {
fetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve([
{ id: 1, name: 'An' },
{ id: 2, name: 'Bình' }
])
});
render(<UserList />);
// Chờ loading kết thúc
expect(screen.getByText('Loading...')).toBeInTheDocument();
await waitFor(() => {
expect(screen.getByText('An')).toBeInTheDocument();
expect(screen.getByText('Bình')).toBeInTheDocument();
});
});
it('hiển thị lỗi khi API fail', async () => {
fetch.mockRejectedValueOnce(new Error('Network error'));
render(<UserList />);
await waitFor(() => {
expect(screen.getByText(/error/i)).toBeInTheDocument();
});
});
});📝 Tóm Tắt
- Vitest + React Testing Library: bộ đôi test hiện đại
- Test behavior (user thấy gì) thay vì implementation
screen.getByText/getByRole: query elementsuserEvent: simulate user interactionswaitFor: test async operations