Розробка Unit-тестів для фронтенду (Jest)

Нестабільні unit-тести та падаючий CI гальмують розробку і змушують команду уникати рефакторингу. Ми налаштовуємо Jest та React Testing Library, проєктуємо тестовану архітектуру і покриваємо критичні модулі надійними тестами. Наша команда реалізує проєкт під ключ — від конфігурації до супроводу, забезпечуючи стабільний зелений статус і впевненість у коді.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Розробка Unit-тестів для фронтенду (Jest)
Середній
~3-5 днів
Часті запитання

Ви запускаєте npm test, а 15 тестів падають через нестабільні моки або неправильну конфігурацію. Знайома ситуація? Ми допомагаємо налагодити надійне unit-тестування: налаштовуємо Jest, підключаємо правильні утиліти та покриваємо критичні модулі тестами. В результаті ваша команда перестає боятися рефакторингу, а CI видає стабільний зелений статус. Наша команда має 7+ років досвіду в автоматизації тестування фронтенд та виконала 50+ проектів з unit-тестів для фронтенду. Ми працюємо з проектами будь-якої складності: від односторінкових додатків до великих корпоративних порталів. Ми не просто пишемо тести — ми проектуємо тестовану архітектуру.

За статистикою наших проектів, впровадження unit-тестів скорочує час регресійного тестування на 40% і зменшує кількість багів у продакшені на 25%. Це пряма економія бюджету на QA та підтримку — в середньому 2-3 місяці роботи QA, що становить економію від $2k–5kів на рік на команду з 5 розробників. Для команди з 5 розробників річна економія може сягати $15,000, а для команди з 10 розробників — до $30,000. Більше того, добре написані тести служать документацією та спрощують онбординг розробників. Вартість базового пакету починається від 30 000 грн, розширеного — від 60 000 грн.

Однак багато команд стикаються з типовими труднощами: нестабільні тести через невірні моки, відсутність покриття кастомних хуків і складна конфігурація Jest з TypeScript. Розберемося, як вирішити ці проблеми, на прикладі наших проектів.

Проблеми, які вирішує unit-тестування

  • Нестабільні тести через невірні mock'и та брудне оточення. Типовий проект з 20-30 компонентами вимагає акуратного управління станом та моками.
  • Відсутність покриття для кастомних хуків та ред'юсерів — логіка залишається без захисту. Без тестів кожен рефакторинг — ризик.
  • Складна конфігурація Jest+React+TypeScript — плутанина з трансформацією, аліасами та стилями. Ми налаштовуємо все від jest.config.ts до setupFilesAfterFramework, використовуючи сучасний стек: Jest 29, @swc/jest для швидкої трансформації, React Testing Library для компонентів, MSW для моків API.

Налаштування займає 2-4 години, а результат — стабільний CI та відчутна економія бюджету. Unit-тестування краще за ручне регресійне тестування в 5 разів за вартістю.

Як налаштувати Jest для React-проєкту з TypeScript?

Стандартна конфігурація включає:

npm install -D jest @types/jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom 
// jest.config.ts
export default {
  testEnvironment: 'jsdom',
  setupFilesAfterFramework: ['<rootDir>/jest.setup.ts'],
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1',
    '\.(css|scss)$': 'identity-obj-proxy',
  },
  transform: {
    '^.+\\.(ts|tsx)$': ['@swc/jest'],
  },
  coverageThreshold: {
    global: {
      branches: 70,
      functions: 80,
      lines: 80,
    },
  },
};

Ця конфігурація підтримує аліаси, CSS-модулі та TypeScript. coverageThreshold гарантує мінімальний рівень покриття — інакше пайплайн впаде. Докладніше про налаштування можна прочитати в Офіційна документація Jest.

Для проектів на Next.js або Vue знадобляться невеликі зміни: додайте testEnvironment: 'jsdom' та налаштуйте трансформацію під ваш фреймворк. Ми підбираємо конфіг індивідуально.

Як тестувати кастомні хуки та асинхронні запити?

Кастомні хуки містять бізнес-логіку, яка повторюється в різних компонентах. Без тестів ви ризикуєте отримати неочікувану поведінку при зміні стейту. API-запити — вузьке місце: змінюється бекенд, падає таймаут, повертається 404. MSW перехоплює запити на рівні мережі, що швидше та надійніше за ручні mock'и. Використання MSW краще за ручні моки в 3 рази за продуктивністю та надійністю.

Порівняння підходів до мокування API

Інструмент Тип моку Продуктивність Реалістичність
MSW Service Worker / Node.js Висока (нативне перехоплення) Повна (емуляція мережі)
jest.mock Підміна модуля Середня Часткова (немає HTTP-статусів)
nock HTTP-перехоплення Висока Повна (але поза браузером)

MSW працює в 2-3 рази швидше за ручні mock'и, а також точніше імітує поведінку сервера.

Що входить у роботу

  • Налаштований Jest з конфігом під ваш стек.
  • 30–50 unit-тестів на утиліти, хуки, компоненти та сервіси.
  • Інтеграція з CI/CD (GitHub Actions, GitLab CI, Jenkins).
  • Документація щодо запуску, додавання нових тестів та роботи з моками.
  • Гарантія стабільності: тести проходять, coverage не нижче обумовленого порогу (зазвичай 80%).

Приклади тестів

Тестування утиліт

// src/utils/currency.ts
export const formatCurrency = (amount: number, locale = 'ru-RU', currency = 'USD') =>
  new Intl.NumberFormat(locale, { style: 'currency', currency }).format(amount);

// src/utils/currency.test.ts
describe('formatCurrency', () => {
  it('formats USD correctly', () => {
    expect(formatCurrency(1500)).toMatch('1 500');
  });

  it('handles zero', () => {
    expect(formatCurrency(0)).toMatch('0');
  });

  it('formats USD', () => {
    expect(formatCurrency(99.99, 'en-US', 'USD')).toBe('$99.99');
  });
});

Тестування React-компонентів

// components/Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Button } from './Button';

describe('Button', () => {
  it('renders label', () => {
    render(<Button>Зберегти</Button>);
    expect(screen.getByRole('button', { name: 'Зберегти' })).toBeInTheDocument();
  });

  it('calls onClick', async () => {
    const onClick = jest.fn();
    render(<Button onClick={onClick}>Click me</Button>);
    await userEvent.click(screen.getByRole('button'));
    expect(onClick).toHaveBeenCalledTimes(1);
  });

  it('disabled button does not fire onClick', async () => {
    const onClick = jest.fn();
    render(<Button onClick={onClick} disabled>Disabled</Button>);
    await userEvent.click(screen.getByRole('button'));
    expect(onClick).not.toHaveBeenCalled();
  });

  it('shows loading spinner when loading', () => {
    render(<Button loading>Save</Button>);
    expect(screen.getByRole('button')).toHaveAttribute('aria-busy', 'true');
    expect(screen.getByTestId('spinner')).toBeInTheDocument();
  });
});

Тестування API-запитів

// services/api.test.ts
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import { fetchUser } from './api';

const server = setupServer(
  rest.get('/api/users/:id', (req, res, ctx) => {
    return res(ctx.json({ id: 1, name: 'Іван' }));
  })
);

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

test('fetchUser returns user data', async () => {
  const user = await fetchUser(1);
  expect(user.name).toBe('Іван');
});

test('fetchUser handles 404', async () => {
  server.use(
    rest.get('/api/users/:id', (req, res, ctx) => res(ctx.status(404)))
  );
  await expect(fetchUser(999)).rejects.toThrow('Not found');
});

Процес роботи

  1. Аналіз проекту: структура, основні модулі, критичні шляхи. Займає 2–4 години.
  2. Налаштування Jest та тестового оточення. Підключаємо бібліотеки, прописуємо аліаси.
  3. Написання тестів (2–4 дні) з пріоритетом на бізнес-логіку. Ми інтегруємо Jest у проект, пишемо тести для React хуків та налаштовуємо CI/CD тестування.
  4. Інтеграція в CI — додавання кроку npm test -- --coverage.
  5. Передача документації та консультація команди.

Рядки виконання

Обсяг робіт Рядків
Базовий (налаштування + 30–40 тестів) 3–5 днів
Розширений (додатково тести на складні сценарії) 6–10 днів
Повне покриття legacy-проєкту від 2 тижнів

Точні строки називаємо після аудиту вашого проекту. Залиште заявку — ми оцінимо обсяг і запропонуємо варіанти.

Типові помилки при написанні тестів - Занадто багато моків, що робить тести крихкими. - Відсутність тестів на edge cases. - Неправильне використання async/await у тестах.

Зв'яжіться з нами для консультації — розкажемо, як покращити покриття та прискорити CI. Замовте unit-тести під ключ і отримайте стабільний продакшен.

Наші компетенції:

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1542
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1369
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1142
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1060