Раздел 29 · Тестирование

Тесты в CI: кэш, шардинг, coverage gate и удобство работы

middle~40 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Тесты в CI: кэш, шардинг, coverage gate и удобство работы

Сцена · конвейер с воротами

Каждый коммит едет по ленте через ворота. Первые ворота: линтер. Дальше: типы. Дальше: тесты. Последние: порог покрытия. Красный на любых воротах останавливает ленту, и код не доезжает до ветки, в которую целился. Локально ты можешь забыть прогнать проверку, CI не забывает никогда. Это и есть его смысл: объективная приёмка, одинаковая для всех.

В этом уроке соберём пайплайн для Kassa и заодно наладим удобство локальной работы, чтобы CI не был единственным местом, где ты видишь результат тестов.

Карта урока · что заберёшь

  • разложишь CI-пайплайн на шаги и поймёшь роль кэша и шардинга;
  • научишься ставить coverage gate как порог в PR;
  • поймёшь, почему порог это не 100 процентов;
  • наладишь watch mode, фокус на одном тесте и дебаг в IDE.

Раздел 1 · пайплайн по шагам

Идея словами

CI это последовательность шагов, где каждый следующий имеет смысл только при зелёном предыдущем. Сначала дешёвое и быстрое (линт, типы), потом дорогое (тесты). Так обратная связь приходит раньше: незачем гонять весь набор, если код даже не типизируется.

Два приёма ускоряют ленту. Кэш зависимостей убирает повторную установку. Шардинг разбивает набор на части и раскидывает по машинам.

Шаг 1 · минимальный пайплайн

# .github/workflows/ci.yml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm lint
      - run: pnpm typecheck
      - run: pnpm exec vitest run --coverage
      - uses: actions/upload-artifact@v4
        with:
          name: coverage
          path: coverage/

cache: pnpm хранит зависимости между прогонами. Порядок шагов не случаен: линт и типы дёшевы и стоят первыми, тесты последними. Отчёт о покрытии уезжает артефактом, чтобы его можно было открыть из PR.

Шаг 2 · шардинг на несколько машин

# машина 1 из 2
vitest run --shard=1/2
# машина 2 из 2
vitest run --shard=2/2

Каждая машина берёт свою половину набора. На большом наборе это превращает десять минут в пять. Шардинг работает с любыми тестами, в том числе эффектными: для CI тест на Effect это обычный Vitest-тест.

Что взять с собой

  • дешёвые шаги (линт, типы) идут раньше дорогих (тесты);
  • кэш зависимостей убирает повторную установку;
  • шардинг режет время прогона почти в число машин.

Раздел 2 · coverage gate

Идея словами

Coverage gate превращает покрытие из информации в правило: ниже порога, и PR не вливается. Это защита от молчаливой деградации, когда новый код приезжает без тестов и общий процент тихо ползёт вниз.

Порог почти никогда не ставят на 100. Сотня заставляет покрывать тривиальный glue и геттеры, и команда начинает писать пустые тесты ради цифры. Разумный порог (часто около 80) держит планку, не превращая её в фетиш. И помни главную оговорку серии: высокий процент покрытия это ещё не честный набор. Что с этим делать, разберём в следующем уроке про mutation testing.

Шаг 1 · порог в конфиге

// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    coverage: {
      provider: 'v8',
      thresholds: { lines: 80, branches: 80, functions: 80 },
    },
  },
});

Теперь pnpm test --coverage сам валит сборку, если покрытие просело ниже порога. Правило живёт в репозитории, а не в голове ревьюера.

Что взять с собой

  • coverage gate блокирует merge при просадке покрытия;
  • порог обычно не 100, чтобы не плодить пустые тесты;
  • высокое покрытие это ещё не честный набор.

Ворота это не магия конфига, а несколько строк кода между списком результатов и кодом возврата. Ниже редактор: собери репортер, который считает исходы, показывает самых медленных и решает, красная сборка или зелёная. Тесты гоняются прямо в браузере, ставить ничего не нужно.

дальше после входа

Урок целиком открыт всем, кто вошёл. Войди по своему email, чтобы читать дальше.

Вход пока по приглашению. Если хочешь учиться, напиши Vassiliy.