Ви запускаєте npm test, а 15 тестів падають через нестабільні моки або неправильну конфігурацію. Знайома ситуація? Ми допомагаємо налагодити надійне unit-тестування: налаштовуємо Jest, підключаємо правильні утиліти та покриваємо критичні модулі тестами. В результаті ваша команда перестає боятися рефакторингу, а CI видає стабільний зелений статус. Наша команда має 7+ років досвіду в автоматизації тестування фронтенд та виконала 50+ проектів з unit-тестів для фронтенду. Ми працюємо з проектами будь-якої складності: від односторінкових додатків до великих корпоративних порталів. Ми не просто пишемо тести — ми проектуємо тестовану архітектуру.
За статистикою наших проектів, впровадження unit-тестів скорочує час регресійного тестування на 40% і зменшує кількість багів у продакшені на 25%. Це пряма економія бюджету на QA та підтримку — в середньому 2-3 місяці роботи QA, що становить економію від 200 000 до 500 000 рублів на рік на команду з 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 = 'RUB') =>
new Intl.NumberFormat(locale, { style: 'currency', currency }).format(amount);
// src/utils/currency.test.ts
describe('formatCurrency', () => {
it('formats RUB 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');
});
Процес роботи
- Аналіз проекту: структура, основні модулі, критичні шляхи. Займає 2–4 години.
- Налаштування Jest та тестового оточення. Підключаємо бібліотеки, прописуємо аліаси.
- Написання тестів (2–4 дні) з пріоритетом на бізнес-логіку. Ми інтегруємо Jest у проект, пишемо тести для React хуків та налаштовуємо CI/CD тестування.
- Інтеграція в CI — додавання кроку
npm test -- --coverage. - Передача документації та консультація команди.
Строки виконання
| Обсяг робіт | Строк |
|---|---|
| Базовий (налаштування + 30–40 тестів) | 3–5 днів |
| Розширений (додатково тести на складні сценарії) | 6–10 днів |
| Повне покриття legacy-проекту | від 2 тижнів |
Точні строки називаємо після аудиту вашого проекту. Залиште заявку — ми оцінимо обсяг і запропонуємо варіанти.
Типові помилки при написанні тестів
- Занадто багато моків, що робить тести крихкими. - Відсутність тестів на edge cases. - Неправильне використання async/await у тестах.Зв'яжіться з нами для консультації — розкажемо, як покращити покриття та прискорити CI. Замовте unit-тести під ключ і отримайте стабільний продакшен.







