Как cделать тестирование кода более эффективным: принципы F.I.R.S.T

от автора

В последнее время я все больше уделяю внимание юнит тестированию, что связано с моим наставничеством на 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *