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

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

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

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка Unit-тестів для фронтенду (Jest)
Середня
~3-5 робочих днів
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

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

Jest — стандарт unit-тестування для JavaScript-проектів. Покриває ізольовану логіку: утиліти, хуки, редьюсери, компоненти. Вбудований у Create React App, працює з Next.js, Vite.

Налаштування

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 },
    },
};

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

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

// src/utils/currency.test.ts
describe('formatCurrency', () => {
    it('formats UAH 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();
    });
});

Тестування кастомних хуків

// hooks/useCounter.test.ts
import { renderHook, act } from '@testing-library/react';
import { useCounter } from './useCounter';

describe('useCounter', () => {
    it('initializes with given value', () => {
        const { result } = renderHook(() => useCounter(5));
        expect(result.current.count).toBe(5);
    });

    it('increments', () => {
        const { result } = renderHook(() => useCounter(0));
        act(() => result.current.increment());
        expect(result.current.count).toBe(1);
    });

    it('respects max value', () => {
        const { result } = renderHook(() => useCounter(10, { max: 10 }));
        act(() => result.current.increment());
        expect(result.current.count).toBe(10);
    });
});

Моки 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');
});

Coverage та CI

# .github/workflows/test.yml
- name: Run tests
  run: npm test -- --coverage --ci --maxWorkers=2

- name: Upload coverage
  uses: codecov/codecov-action@v3

Часовий графік

Налаштування Jest + перші 30–50 тестів для існуючого проекту: 3–5 днів.