В последнее время я все больше уделяю внимание юнит тестированию, что связано с моим наставничеством на Hexlet и выравнивание пирамиды на работе. И немного решил освежить основы при написании юнит тестов:
Быстрота (Fast)
Тесты должны выполняться очень быстро. Время выполнения, включая настройку, сам тест и завершение, должно составлять миллисекунды, так как в проекте может быть тысячи тестов.
Изоляция (Isolated/Independent)
Каждый тест должен быть независим. Он должен следовать модели «подготовка, действие, проверка» (Arrange, Act, Assert) без зависимости от других тестов или внешнего окружения.
Повторяемость (Repeatable)
Тесты должны давать одинаковые результаты в любой среде и в любое время, независимо от внешних условий, таких как дата/время или случайные значения.
Самодостаточность (Self-Validating)
Результаты теста должны быть ясны без внешних проверок — тест либо проходит, либо нет, без всякой необходимости в дополнительной интерпретации.
Тщательность (Thorough/Timely)
Тесты должны охватывать все возможные сценарии использования и граничные условия, не ограничиваясь простым стремлением к 100% покрытию кода. Важно тестировать различные объемы данных, безопасность, и корректную обработку исключений.
Реальные примеры тестирования в React с использованием принципов FIRST
1. Быстрота (Fast)
// ❌ Плохой тест, который загружает данные из API it('fetches and displays user data', async () => { render(<UserProfile userId="123" />); await waitFor(() => expect(screen.getByText(/Username/)).toBeInTheDocument()); });
Этот тест медленный, потому что делает реальный запрос к API.
Хороший пример:
// ✅ Использование моков для ускорения тестов, // чтобы сделать тест быстрым и независимым jest.mock('api/userApi'); it('displays user data from mock', () => { userApi.getUser.mockResolvedValue({ id: '123', name: 'John Doe' }); render(<UserProfile userId="123" />); expect(screen.getByText(/John Doe/)).toBeInTheDocument(); });
2. Изоляция (Isolated/Independent)
Плохой пример:
// ❌ Эти тесты зависимы друг от друга из-за общего состояния хука const { result } = renderHook(() => useCounter()); it('increments counter', () => { act(() => { result.current.increment(); }); expect(result.current.count).toBe(1); }); it('increments counter again', () => { act(() => { result.current.increment(); }); expect(result.current.count).toBe(2); });
Хороший пример:
// ✅ Каждый тест изолирован it('increments counter', () => { const { result } = renderHook(() => useCounter()); act(() => { result.current.increment(); }); expect(result.current.count).toBe(1); }); it('increments counter independently', () => { const { result } = renderHook(() => useCounter()); act(() => { result.current.increment(); }); expect(result.current.count).toBe(1); });
3. Повторяемость (Repeatable)
Плохой пример:
// ❌ Тест зависит от текущей даты it('shows current date', () => { render(<CurrentDateDisplay />); const today = new Date().toISOString().slice(0, 10); expect(screen.getByText(today)).toBeInTheDocument(); });
Этот тест даст разные результаты каждый день.
Хороший пример:
// ✅ Использование фиксированной даты в тестах, // что обеспечивает повторяемость результатов it('shows current date', () => { jest.useFakeTimers().setSystemTime(new Date('2024-01-01')); render(<CurrentDateDisplay />); expect(screen.getByText('2024-01-01')).toBeInTheDocument(); });
4. Самодостаточность (Self-Validating)
Плохой пример:
// ❌ Тест не автоматизирован полностью it('renders correctly', () => { const component = render(<MyComponent />); console.log(component); // Требует проверки вывода });
Хороший пример:
// ✅ Тест с проверкой it('renders correctly', () => { render(<MyComponent />); expect(screen.getByText('Hello World')).toBeInTheDocument(); });
Этот тест полностью самодостаточен и не требует внешних действий для проверки.
5. Тщательность (Thorough/Timely)
Плохой пример:
// ❌ Тест проверяет только одно состояние компонента it('shows loading state', () => { render(<DataLoader />); expect(screen.getByText('Loading...')).toBeInTheDocument(); });
Тест ограничен только проверкой состояния загрузки.
Хороший пример:
// ✅ Эти тесты охватывают разные возможные состояния компонента it('shows loading state', () => { render(<DataLoader />); expect(screen.getByText('Loading...')).toBeInTheDocument(); }); it('displays data after loading', async () => { const mockData = { text: 'Data loaded' }; fetchData.mockResolvedValue(mockData); render(<DataLoader />); expect(await screen.findByText('Data loaded')).toBeInTheDocument(); }); it('shows error when data fails to load', async () => { fetchData.mockRejectedValue('Error loading data'); render(<DataLoader />); expect(await screen.findByText('Error loading data')).toBeInTheDocument(); });
Присоединяйтесь в мой tg, там я пишу различные заметки по QA
ссылка на оригинал статьи https://habr.com/ru/articles/830962/
Добавить комментарий