PurpleSchool — курсы программирования онлайн
  • Бесплатно
    • Курсы
    • JavaScript Основы разработкиPython Основы PythonCSS CSS FlexboxКарта развития
    • База знанийИконка стрелки
    • Новостные рассылкиИконка стрелки
  • Карьерные пути
    • Frontend React разработчик
    • Frontend Vue разработчик
    • Backend разработчик Node.js
    • Fullstack разработчик React / Node.js
    • Mobile разработчик React Native
    • Backend разработчик Golang
    • Devops инженер
    • Backend разработчик Python
  • О нас
    • Отзывы
    • Реферальная программа
    • О компании
    • Контакты
  • Иконка открытия меню
    • Сообщество
    • PurpleПлюс
    • AI тренажёр
    • Проекты
PurpleSchool — платформа бесплатных roadmap и курсов для разработчиков
ютуб иконка
Telegram иконка
VK иконка
VK иконка
Курсы
ГлавнаяКаталог курсовFrontendBackendFullstack
Практика
КарьераПроектыPurpleПлюс
Материалы
БлогБаза знаний
Документы
Договор офертаПолитика конфиденциальностиПроверка сертификатаМиграция курсовРеферальная программа
Реквизиты
ИП Ларичев Антон АндреевичИНН 773373765379contact@purpleschool.ru

PurpleSchool © 2020 -2026 Все права защищены

  • Курсы
    • FrontendИконка стрелки
    • AI разработкаИконка стрелки
    • BackendИконка стрелки
    • DevOpsИконка стрелки
    • MobileИконка стрелки
    • ТестированиеИконка стрелки
    • Soft-skillsИконка стрелки
    • ДизайнИконка стрелки
    Иконка слояПерейти в каталог курсов
  • PurpleSchool — курсы программирования онлайн
    • Сообщество
    • PurpleПлюс
    • AI тренажёр
    • Проекты
    Главная
    Сообщество
    Как внедрить тесты в проект, где их никогда не было: пошаговая стратегия

    Как внедрить тесты в проект, где их никогда не было: пошаговая стратегия

    Аватар автора Как внедрить тесты в проект, где их никогда не было: пошаговая стратегия

    Антон Ларичев

    Иконка календаря14 апреля 2026
    testingjavascripttypescriptmiddleИконка уровня middle
    Картинка поста Как внедрить тесты в проект, где их никогда не было: пошаговая стратегия

    Введение

    Внедрить тесты в проект, где их никогда не было, — одна из самых частых задач для middle-разработчика. Вы приходите на проект, видите тысячи строк кода без единого теста и задаётесь вопросом: с чего вообще начать тестирование legacy-кода? Попытка покрыть всё разом приводит к выгоранию и заброшенным тестам.

    В этой статье разберём пошаговую стратегию внедрения тестов в существующий проект на JavaScript или TypeScript. Без фанатизма, без переписывания всего кода — только практичный подход, который работает в реальных командах.

    Почему нельзя просто взять и покрыть всё тестами

    Первый порыв — написать тесты на всё сразу. Это ошибка. В проекте без тестов код, как правило, тесно связан: функции зависят друг от друга, модули напрямую обращаются к базе данных, конфигурация вшита в бизнес-логику. Такой код сложно тестировать без рефакторинга.

    Вместо этого нужна стратегия тестирования, которая даёт результат с первого дня и постепенно наращивает покрытие.

    Шаг 1: настройка инфраструктуры тестирования

    Прежде чем писать первый тест, подготовьте окружение. Для JavaScript и TypeScript проектов Vitest — оптимальный выбор: быстрый, совместимый с ESM, минимальная настройка.

    Установка и конфигурация Vitest

    npm install -D vitest
    

    Добавьте конфигурацию в корень проекта:

    // vitest.config.ts
    import { defineConfig } from 'vitest/config';
    
    export default defineConfig({
      test: {
        globals: true,
        environment: 'node',
        coverage: {
          provider: 'v8',
          reporter: ['text', 'html'],
          // Не ставьте порог покрытия сразу — он будет расти постепенно
        },
      },
    });
    

    Добавьте скрипты в package.json:

    {
      "scripts": {
        "test": "vitest",
        "test:run": "vitest run",
        "test:coverage": "vitest run --coverage"
      }
    }
    

    Настройте CI-пайплайн так, чтобы тесты запускались на каждый пуш. Даже если тестов пока один — пайплайн должен работать с самого начала.

    Шаг 2: определить критические зоны для первых тестов

    Не пишите тесты на всё подряд. Начните с кода, который ломается чаще всего или несёт наибольшие бизнес-риски.

    Как выбрать, что тестировать первым

    Проанализируйте проект по трём критериям:

    • Частота багов — посмотрите историю коммитов и тикетов. Модули с частыми фиксами — кандидаты номер один.
    • Бизнес-критичность — оплата, регистрация, авторизация. Ошибка здесь стоит дороже всего.
    • Частота изменений — код, который меняется каждую неделю, нуждается в защите от регрессий.
    # Найти файлы с наибольшим количеством коммитов
    git log --pretty=format: --name-only | sort | uniq -c | sort -rn | head -20
    

    Эта команда покажет файлы, которые менялись чаще всего — именно с них стоит начать покрытие тестами.

    Шаг 3: написать первые unit-тесты для чистых функций

    В любом проекте есть функции, которые принимают данные и возвращают результат без побочных эффектов. Валидаторы, форматтеры, калькуляторы, парсеры — это идеальные кандидаты для первых unit-тестов.

    // src/utils/price.ts
    export function formatPrice(amount: number, currency: string = 'RUB'): string {
      if (amount < 0) throw new Error('Сумма не может быть отрицательной');
      return new Intl.NumberFormat('ru-RU', {
        style: 'currency',
        currency,
      }).format(amount);
    }
    
    export function calculateDiscount(price: number, percent: number): number {
      if (percent < 0 || percent > 100) {
        throw new Error('Процент скидки должен быть от 0 до 100');
      }
      return Math.round(price * (1 - percent / 100));
    }
    

    Тесты для этих функций:

    // src/utils/price.test.ts
    import { describe, it, expect } from 'vitest';
    import { formatPrice, calculateDiscount } from './price';
    
    describe('formatPrice', () => {
      it('форматирует цену в рублях', () => {
        const result = formatPrice(1500);
        expect(result).toContain('1');
        expect(result).toContain('500');
      });
    
      it('выбрасывает ошибку для отрицательной суммы', () => {
        expect(() => formatPrice(-100)).toThrow('Сумма не может быть отрицательной');
      });
    });
    
    describe('calculateDiscount', () => {
      it('рассчитывает скидку 20%', () => {
        expect(calculateDiscount(1000, 20)).toBe(800);
      });
    
      it('возвращает полную цену при скидке 0%', () => {
        expect(calculateDiscount(500, 0)).toBe(500);
      });
    
      it('отклоняет невалидный процент', () => {
        expect(() => calculateDiscount(100, 150)).toThrow();
      });
    });
    

    Такие тесты пишутся за минуты, не требуют рефакторинга основного кода и сразу дают уверенность в корректности.

    Шаг 4: добавить интеграционные тесты для критичных сценариев

    После unit-тестов переходите к интеграционным. Они проверяют взаимодействие модулей — например, что сервис корректно вызывает репозиторий и обрабатывает результат.

    Для изоляции внешних зависимостей используйте моки Vitest:

    // src/services/order.test.ts
    import { describe, it, expect, vi } from 'vitest';
    import { OrderService } from './order.service';
    import { OrderRepository } from '../repositories/order.repository';
    
    // Мокаем модуль репозитория
    vi.mock('../repositories/order.repository');
    
    describe('OrderService', () => {
      it('создаёт заказ и возвращает его ID', async () => {
        // Настраиваем мок
        const mockCreate = vi.fn().mockResolvedValue({ id: 42, status: 'created' });
        vi.mocked(OrderRepository).mockImplementation(() => ({
          create: mockCreate,
        }) as any);
    
        const service = new OrderService(new OrderRepository());
        const result = await service.createOrder({ productId: 1, quantity: 2 });
    
        expect(result.id).toBe(42);
        expect(mockCreate).toHaveBeenCalledWith({
          productId: 1,
          quantity: 2,
        });
      });
    });
    

    Шаг 5: внедрить правило — новый код только с тестами

    Самый важный шаг в стратегии тестирования. Договоритесь с командой: любой новый код или исправление бага сопровождается тестом. Это правило не требует покрывать старый код, но гарантирует, что покрытие растёт с каждым коммитом.

    Добавьте проверку в CI:

    # .github/workflows/test.yml
    name: Tests
    on: [push, pull_request]
    jobs:
      test:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - uses: actions/setup-node@v4
            with:
              node-version: 20
          - run: npm ci
          - run: npm run test:run
    

    Когда тесты блокируют мёрж, команда быстро привыкает писать их.

    Шаг 6: постепенно повышать покрытие тестами

    После того как инфраструктура работает и новый код пишется с тестами, начинайте наращивать покрытие старого кода. Используйте отчёт coverage для поиска непокрытых участков:

    npm run test:coverage
    

    Установите начальный порог покрытия чуть выше текущего уровня и повышайте его каждый спринт:

    // vitest.config.ts
    coverage: {
      thresholds: {
        statements: 30, // Начните с реального значения
        branches: 25,
        functions: 30,
        lines: 30,
      },
    }
    

    Каждые две недели увеличивайте пороги на 3-5%. Через полгода покрытие вырастет до приемлемого уровня без стресса для команды.

    Частые ошибки при внедрении тестов в legacy-проект

    Попытка покрыть всё за спринт. Это приводит к сотням хрупких тестов, которые ломаются при каждом изменении. Внедряйте тесты постепенно.

    Тестирование деталей реализации. Тест, который проверяет порядок вызовов приватных методов, сломается при любом рефакторинге. Тестируйте поведение, а не реализацию.

    Игнорирование негативных сценариев. Тесты только на happy path дают ложную уверенность. Проверяйте невалидные данные, ошибки сети, пустые ответы.

    Отсутствие тестов в CI. Тесты, которые запускаются только локально, со временем перестают запускаться вообще. CI — обязательное условие.

    Заключение

    Внедрить тесты в проект без тестов — задача не на один день, а долгосрочная стратегия. Начните с настройки Vitest и CI-пайплайна. Напишите первые тесты для чистых функций и критичной бизнес-логики. Введите правило: новый код идёт только с тестами. Постепенно повышайте порог покрытия. Через несколько месяцев вы получите проект, в котором тесты — не обуза, а надёжная страховка от регрессий при каждом релизе.

    Иконка глаза12

    Комментарии

    0

    Постройте личный план изучения CSS Flexbox - полный курс и практика его использования до уровня Middle — бесплатно!

    CSS Flexbox - полный курс и практика его использования — часть карты развития Frontend

    • step100+ шагов развития
    • lessons30 бесплатных лекций
    • lessons300 бонусных рублей на счет

    Бесплатные лекции

    Лучшие курсы по теме

    изображение курса

    Основы JavaScript

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    Продвинутый JavaScript

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.9
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    TypeScript с нуля

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее

    Похожие статьи

    Картинка поста Тестирование API: от unit-тестов до e2e с Playwright и Vitest
    Иконка аватараАнтон
    Иконка календаря13 апреля 2026
    testingnodejstypescriptmiddleИконка уровня middle

    Тестирование API: от unit-тестов до e2e с Playwright и Vitest

    Как выстроить тестирование API на практике: unit-тесты в Vitest, интеграционные проверки с MSW и e2e-сценарии в Playwright. Примеры на TypeScript с полным покрытием.

    Иконка чипа0
    Иконка глаза45
    Иконка комментариев0
    Картинка поста Миграция с JavaScript на TypeScript: пошаговое руководство
    Иконка аватараАнтон
    Иконка календаря25 марта 2026
    javascripttypescriptmiddleИконка уровня middle

    Миграция с JavaScript на TypeScript: пошаговое руководство

    Миграция с JavaScript на TypeScript позволяет повысить надежность кода и упростить рефакторинг. Разбираем пошаговый план перевода проекта, настройку tsconfig и частые ошибки.

    Иконка чипа0
    Иконка глаза197
    Иконка комментариев0
    Картинка поста Vue 3 Composition API: 5 реальных паттернов, которые упрощают код
    Иконка аватараАнтон
    Иконка календаря23 марта 2026
    vuetypescriptjavascriptmiddleИконка уровня middle

    Vue 3 Composition API: 5 реальных паттернов, которые упрощают код

    Vue 3 Composition API открывает мощные паттерны для организации и переиспользования логики в компонентах. Разбираем 5 реальных composables с примерами на TypeScript, которые упрощают код в продакшен-проектах.

    Иконка чипа0
    Иконка глаза170
    Иконка комментариев0
    Иконка чипа0