Angular

Тестирование: Vitest и Playwright

Тесты компонента шли 40 секунд на холодном старте: Karma поднимала реальный браузер через WebDriver, грузила Webpack-бандл и только потом запускала спеки. Watch-режим реагировал на правку с заметной паузой. В Angular 21 (ноябрь 2025) дефолтным раннером стал Vitest: он исполняет тесты на esbuild, переиспользует трансформацию из application builder и запускает компоненты в jsdom или в реальном браузере через Playwright по выбору. Тот же тестовый файл с TestBed теперь стартует за доли секунды, а watch пересобирает только затронутое.

  • Angular 21 сделал Vitest рекомендованным раннером по умолчанию, а Karma официально выведена из эксплуатации после многих лет в этой роли
  • Component harnesses из Angular CDK тестируют Material-компоненты без привязки к их внутренней разметке - один и тот же тест переживает рефакторинг шаблона
  • Playwright в крупных проектах гоняет e2e в Chromium, Firefox и WebKit параллельно, с авто-ожиданиями и трейсами при падении
  • Монорепо команд: unit-тесты на Vitest дают мгновенный фидбек в watch, e2e на Playwright проверяют критичные пути в CI
  • Zoneless-приложения: тесты сигналов опираются на явный fixture.detectChanges и whenStable вместо неявной магии Zone.js

Предварительные знания

  • Компоненты, их шаблоны, signal inputs и outputs
  • Базовое понимание автотеста: arrange, act, assert
  • Идея обнаружения изменений (change detection): почему вид обновляется после изменения данных
  • Компоненты

От Karma к Vitest

Karma была раннером Angular с самого начала: она запускала тесты в реальном браузере и долго оставалась стандартом. Со временем её модель устарела - подъём браузера через WebDriver и сборка на Webpack делали запуск медленным, а проект Karma был объявлен устаревшим ещё в 2023 году. Команда Angular искала замену и остановилась на Vitest, раннере из экосистемы Vite на esbuild. В Angular 20 поддержка Vitest появилась экспериментально, а в Angular 21 (ноябрь 2025) он стал рекомендованным раннером по умолчанию для новых проектов, переиспользуя ту же сборочную трансформацию, что и application builder.

Vitest вместо Karma: почему и что изменилось

Karma поднимала настоящий браузер и собирала тесты на Webpack, отчего холодный старт был медленным, а watch-режим реагировал с паузой. Vitest исполняет тесты на esbuild и переиспользует ту же трансформацию, что и application builder Angular, поэтому компиляция быстрая, а пересборка в watch затрагивает только изменённый граф. По умолчанию компоненты исполняются в jsdom - облегчённой реализации DOM в Node, а при необходимости настоящего браузера Vitest подключает browser mode через Playwright.

АспектKarma (прошлое)Vitest (Angular 21)
Среда исполненияРеальный браузер через WebDriverjsdom по умолчанию, браузер по запросу
Сборка тестовWebpackesbuild, та же трансформация, что у билда
Watch-режимПерезапуск, заметная паузаИнкрементально, только затронутое
Статус в Angular 21Выведена из эксплуатацииРекомендованный раннер по умолчанию

API тестов почти не изменился: describe, it, expect, beforeEach остались на месте, и TestBed работает как раньше. Поэтому миграция существующих спеков с Karma на Vitest для большинства проектов сводится к смене конфигурации раннера, а не переписыванию тестов.

Почему Vitest стартует и работает в watch-режиме заметно быстрее, чем Karma?

TestBed и компонентные тесты

TestBed - это движок тестового окружения Angular. Он собирает миниатюрный модуль с нужными провайдерами и компонентами, а метод createComponent поднимает экземпляр компонента в тестовом DOM и возвращает fixture. Из fixture доступны componentInstance (сам объект компонента), nativeElement (корневой DOM-узел) и componentRef.setInput для подачи значений в signal inputs. Метод detectChanges запускает обнаружение изменений и обновляет разметку.

Порядок строгий: настроить модуль, создать компонент, подать входы через setInput, вызвать detectChanges и только потом читать DOM. setInput - правильный способ задать signal input в тесте: он эмулирует привязку от родителя и помечает компонент на проверку. Чтение textContent через ?? '' и optional chaining обходится без приведений типов и non-null.

Частая ошибка - проверять DOM до вызова detectChanges. Сразу после createComponent шаблон ещё не отрисован первым проходом обнаружения изменений, и nativeElement будет пустым. Сначала setInput и detectChanges, потом ассерты по разметке.

Зависимости компонента подменяются через providers в configureTestingModule: реальный сервис заменяется на фейк или объект-заглушку. Для чистого dumb-компонента подменять обычно нечего - в этом и состоит выгода разделения smart/dumb для тестируемости.

В тесте сразу после TestBed.createComponent проверяется nativeElement.textContent, но он пустой. В чём причина?

Component harnesses: тесты без привязки к разметке

Тест, который ищет элементы по CSS-селекторам внутренней разметки компонента, ломается при любом рефакторинге шаблона. Component harness из Angular CDK решает это: harness - это стабильный объектный API над компонентом, через который тест взаимодействует с ним как пользователь, не зная внутреннего HTML. У Material-компонентов есть готовые harness, и для своих компонентов можно написать собственный.

Тест находит кнопку по её тексту, кликает и проверяет состояние disabled - всё через методы harness, ни одного CSS-селектора внутренней разметки. Если Material поменяет внутренний HTML кнопки в новой версии, harness обновится вместе с библиотекой, а тест останется рабочим. Это разрывает связь теста с деталями реализации компонента.

Harness работает в двух средах через единый API: TestbedHarnessEnvironment для unit-тестов в Vitest и аналог для e2e. Один и тот же набор обращений к компоненту можно переиспользовать на разных уровнях пирамиды тестов.

В чём главное преимущество тестирования через component harness вместо поиска элементов по CSS-селекторам внутренней разметки?

Playwright для e2e и тесты в zoneless

Unit- и компонентные тесты проверяют части в изоляции, но не отвечают на вопрос работает ли приложение целиком в настоящем браузере. Это задача end-to-end тестов, и стандартный инструмент для них в современном Angular - Playwright. Он запускает приложение в реальных Chromium, Firefox и WebKit, имитирует действия пользователя и автоматически ждёт появления элементов, что убирает целый класс flaky-тестов из ручных задержек.

Playwright обращается к элементам по доступной роли и имени (getByRole), как это делает скринридер, поэтому e2e заодно проверяют доступность. Метод expect с toBeVisible встроенно ждёт, пока элемент появится, без ручных таймаутов. При падении Playwright сохраняет трейс со скриншотами и логом действий, что резко ускоряет разбор.

Отдельный нюанс - тестирование в zoneless-режиме. Без Zone.js Angular не перехватывает асинхронность неявно, поэтому в компонентных тестах вид обновляют явным вызовом fixture.detectChanges, а завершения асинхронных задач ждут через await fixture.whenStable. Сигналы при этом тестируются прямо: установить writable-сигнал, вызвать detectChanges и проверить, что зависимый computed и разметка обновились.

Здоровая пирамида: много быстрых unit- и компонентных тестов на Vitest внизу, заметно меньше e2e на Playwright наверху - только критичные пользовательские пути. E2e дорогие и медленные, поэтому ими покрывают сквозные сценарии, а логику деталей оставляют unit-тестам.

Чем отличается тестирование обновления вида в zoneless-режиме от привычного режима с Zone.js?

Связь с другими темами

Урок про то, как проверять то, что построено в остальных уроках. Связи:

  • Архитектура состояния — Smart/dumb разделение делает dumb-компоненты тривиальными для unit-тестов через signal inputs
  • Обнаружение изменений и zoneless — В zoneless тесты опираются на явный detectChanges, понимание CD здесь обязательно
  • Сборка и CLI — Vitest переиспользует трансформацию application builder, поэтому тесты и сборка делят пайплайн

Итог

  • В Angular 21 Vitest заменил Karma как рекомендованный раннер по умолчанию: запуск на esbuild, watch только по затронутому
  • TestBed настраивает тестовый модуль и создаёт компоненты, fixture даёт доступ к экземпляру и DOM
  • Component harnesses (CDK) тестируют компоненты через стабильный API, не завязываясь на внутреннюю разметку
  • Playwright покрывает end-to-end: реальный браузер, авто-ожидания, кросс-браузерность, трейсы при падении
  • В zoneless обновление вида вызывается явно через fixture.detectChanges, асинхронность ждут через whenStable
  • Пирамида тестов: много быстрых unit/компонентных на Vitest и точечные e2e на Playwright для критичных путей

Связанные уроки

  • ng-03-components — Компонентные тесты создают и проверяют компоненты, поэтому нужна базовая модель компонентов
  • ng-38-state-architecture — Чистые dumb-компоненты из урока архитектуры тестируются особенно легко через signal inputs
  • ng-21-change-detection-zoneless — Тестирование сигналов в zoneless требует понимания того, как работает обнаружение изменений
Тестирование: Vitest и Playwright

0

1

Войти