Монорепозиторий (monorepo) — это архитектура управления исходным кодом, в которой несколько проектов, приложений и общих библиотек сосуществуют в одном Git-репозитории, управляемом инструментами оркестрации вроде Nx, Turborepo или Lerna, оптимизирующими сборки и тесты на основе графа зависимостей.
Монорепозитории победили. Front-end команды, управляющие несколькими приложениями — маркетинговый сайт, админ-панель, клиентское приложение, портал партнёров — объединяют их в один репозиторий. Это проще для совместного использования кода, более согласованно для design system, эффективнее для сквозных обновлений.
Но для визуального тестирования это логистический кошмар. Не потому что визуальное тестирование не работает в монорепозитории. Оно работает отлично. Проблема в том, что оно работает слишком хорошо: изменение в общем пакете потенциально запускает визуальные тесты для всех зависящих от него проектов. И когда ваш монорепозиторий содержит 15, 30 или 50 проектов, «тестировать всё при каждом коммите» перестаёт быть вариантом.
Фундаментальная проблема: граф зависимостей
В монорепозитории проекты не независимы. Они используют общие пакеты: design system, библиотеку утилит, общие компоненты, разделяемые конфигурации. Эти зависимости образуют граф — и именно этот граф делает визуальное тестирование сложным.
Возьмём конкретный пример. У вас есть пакет «ui-components», содержащий кнопки, формы и компоненты навигации. Этот пакет используется 12 приложениями в вашем монорепозитории. Вы меняете padding кнопки в «ui-components». Визуально это изменение потенциально затрагивает все 12 приложений. Каждая страница, содержащая кнопку, может рендериться по-другому.
Если вы запустите визуальные тесты для всех 12 приложений, вы потенциально захватите сотни скриншотов. CI/CD-пайплайн займёт 45 минут. Разработчики ждут. И если один из тестов провалится — ложное срабатывание из-за проблемы рендеринга в headless-режиме — разработчик пакета «ui-components» должен расследовать сбой в приложении, о котором он даже не знает.
Это невыносимо. И это именно то, что происходит в монорепозиториях без стратегии визуального тестирования.
Тестировать то, что изменилось, а не всё: принцип затронутости
Первое правило визуального тестирования в монорепозитории просто в теории, сложно на практике: тестировать только то, что затронуто изменением.
Современные инструменты монорепозиториев — Nx и Turborepo в первую очередь — умеют вычислять граф зависимостей и определять «затронутые» проекты. Когда вы модифицируете файл в пакете «ui-components», Nx может сказать, что приложения A, C, F и K зависят от этого пакета, а B, D и остальные — нет.
Это основа. Но для визуального тестирования этого недостаточно. Потому что «приложение A зависит от ui-components» не означает, что все страницы приложения A используют изменённый компонент. Если вы изменили кнопку, затронуты только страницы, содержащие кнопку. Страницы без кнопки стабильны.
Интеллектуальное визуальное тестирование в монорепозитории работает на двух уровнях фильтрации. Первый уровень: какие проекты затронуты изменением, по графу зависимостей. Второй уровень: какие страницы или компоненты этих проектов реально используют изменённый элемент.
Первый уровень решается инструментами монорепозитория. Второй уровень значительно сложнее — он требует либо статического анализа кода (какие страницы импортируют какой компонент), либо явного маппинга, поддерживаемого командой (этот компонент используется на этих страницах).
Реальность общих пакетов
Общие пакеты — сила и слабость монорепозитория. Они обеспечивают согласованность: единый design system, единый источник истины для визуальных компонентов. Но они также создают значительный радиус поражения.
Есть три типа общих пакетов, создающих специфические проблемы для визуального тестирования.
Пакеты UI-компонентов. Самый очевидный случай. Изменение кнопки, формы или модального окна визуально затрагивает все страницы, которые их используют. Радиус поражения пропорционален популярности компонента. Layout-компонент, используемый повсюду (Header, Footer, Sidebar), имеет максимальный радиус поражения.
Пакеты стилей и токенов. Изменение design tokens — цветов, отступов, типографики — часто невидимо в традиционном графе зависимостей. Если вы модифицируете основной цвет в файле токенов, граф зависимостей видит изменение в пакете «design-tokens». Но это изменение визуально затрагивает всё: каждую основную кнопку, каждую ссылку, каждый акцентированный элемент во всех приложениях. Радиус поражения тотальный.
Пакеты утилит с визуальными побочными эффектами. Менее очевидные: утилитные функции форматирования дат, обрезки текста или расчёта layout. Изменение функции обрезки текста — с 100 до 80 максимальных символов — имеет прямой визуальный эффект на все компоненты, которые её используют. Но граф зависимостей не знает, что это «визуальное» изменение.
Стратегии, которые работают
Наблюдая за десятками команд, управляющих визуальным тестированием в монорепозиториях, вот стратегии, дающие лучшие результаты.
Стратегия 1: визуальное тестирование по слоям
Организуйте визуальные тесты в три слоя с разной частотой выполнения.
Слой компонентов. Тестируйте каждый компонент design system изолированно, через Storybook или аналог. Эти тесты запускаются только при изменении пакета компонентов. Они быстрые (несколько минут) и защищают саму библиотеку. Это первая страховочная сеть, описанная в статье о визуальном тестировании в design systems.
Слой затронутых страниц. Тестируйте страницы приложений, затронутых изменением, определённых графом зависимостей. Эти тесты запускаются на каждый pull request, но только для затронутых проектов. Они — ядро вашей стратегии.
Полный слой. Тестируйте все страницы всех приложений. Этот слой не запускается на каждый PR — это слишком дорого. Запускайте его раз в день (nightly) или перед каждым релизом. Он ловит регрессии, которые граф зависимостей не смог предсказать.
Стратегия 2: явный маппинг компонент-страница
Поддерживайте конфигурационный файл, связывающий каждый общий компонент со страницами, которые его используют. Когда компонент Button изменяется, файл указывает, что страницы /login, /signup, /checkout и /settings приложения A используют этот компонент. Тестируются только эти страницы.
Этот маппинг утомительно поддерживать вручную. Но он может генерироваться автоматически через статический анализ кода — обходя импорты каждой страницы и поднимаясь по зависимостям. Nx предлагает плагины, упрощающие такой анализ.
Преимущество значительное: вместо тестирования 200 страниц приложения A при изменении компонента вы тестируете только 12. Пайплайн сокращается с 30 минут до 3.
Стратегия 3: общий baseline с переопределением
В монорепозитории с общим design system визуальные baseline (эталонные скриншоты) имеют особенность: общие компоненты имеют одинаковый рендер во всех приложениях (в теории). Можно поддерживать baseline design system на уровне пакета и перезахватывать baseline на уровне приложений только при изменении контекста интеграции.
Конкретно: когда вы модифицируете компонент Button, вы обновляете baseline Button в пакете ui-components. Приложения, использующие Button, наследуют новый baseline. Только приложения, где Button рендерится иначе в контексте (из-за специфичного CSS, темы или переопределения), нуждаются в собственных baseline.
Классические ошибки, которых следует избегать
Тестировать всё, всё время. Естественная реакция и худшая. Пайплайны становятся медленными, разработчики игнорируют результаты из-за шума, реальные баги тонут в ложных срабатываниях. Визуальное тестирование в монорепозитории должно быть хирургическим.
Игнорировать граф зависимостей. Некоторые команды настраивают визуальные тесты независимо от инструмента монорепозитория. Результат: тесты не знают, какие проекты затронуты, и тестируют всё или ничего. Интегрируйте инструмент визуального тестирования с Nx или Turborepo. Используйте их команды «affected» для запуска правильных тестов.
Помещать все baseline в одну папку. Визуальные baseline должны находиться рядом с проектами, которые они защищают. Если вы централизуете все baseline в корневой папке, теряете гранулярность: больше не знаете, какой baseline какому проекту принадлежит, и очистка устаревших baseline становится невозможной.
Пренебрегать версионированием baseline при сквозных обновлениях. Когда вы модифицируете design token, затрагивающий все проекты, все baseline должны обновляться одновременно. Если обновлять их проект за проектом в отдельных PR, создаётся окно, в котором у одних проектов новые baseline, а у других — старые. Тесты становятся несогласованными.
Роль CI/CD в визуальном тестировании монорепозитория
Конфигурация CI/CD критична. Ваш пайплайн должен уметь три вещи.
Во-первых, вычислять набор проектов, затронутых изменением. Это задача Nx (nx affected) или Turborepo (turbo run --filter). Этот шаг определяет периметр тестирования.
Во-вторых, параллелизировать визуальные тесты по проектам. Если затронуты три приложения, запускайте тесты всех трёх параллельно, а не последовательно. У каждого приложения свой набор baseline и свой отчёт о результатах. Параллелизация — единственный способ поддерживать приемлемое время пайплайна.
В-третьих, агрегировать результаты и блокировать PR при необходимости. Даже если тесты параллелизированы, финальный вердикт должен быть единым: PR проходит или нет. Если у приложения A есть визуальная регрессия, а у остальных нет, PR блокируется. Разработчик знает точно, какое приложение затронуто, и может расследовать.
Когда no-code упрощает всё
Инструменты визуального тестирования на основе кода — Playwright, Cypress с визуальными плагинами — хорошо интегрируются в монорепозиторий, так как живут в том же репозитории рядом с тестируемым кодом. Но они добавляют тестовый код для поддержки, конфигурации по проектам и специфические CI/CD-скрипты.
No-code инструменты вроде Delta-QA предлагают другой подход. Вместо тестирования из исходного кода они тестируют развёрнутые URL. Вы настраиваете URL каждого приложения, инструмент захватывает скриншоты и сравнивает с baseline. Монорепозиторий больше не фактор сложности — инструмент видит только URL, а не пакеты.
Недостаток: вы не используете граф зависимостей для фильтрации тестов. Преимущество: конфигурация мгновенная, нет тестового кода для поддержки, тестирование независимо от архитектуры репозитория. Для команд без выделенных разработчиков тестов это часто правильный компромисс.
FAQ
Что лучше для визуального тестирования в монорепозитории: Nx или Turborepo?
Nx имеет значительное преимущество благодаря более детальному графу зависимостей и плагинам статического анализа. Команда nx affected точно определяет затронутые проекты, а исполнители Nx позволяют интегрировать задачи визуального тестирования в граф задач. Turborepo проще, но предлагает менее гранулярную фильтрацию.
Как управлять baseline, когда design token изменяется и затрагивает всё?
Рассматривайте это как сквозное обновление. Создайте выделенный PR, который модифицирует токен и обновляет все затронутые baseline за одну операцию. Запустите полный слой визуальных тестов, подтвердите ожидаемые изменения пакетно и влейте. Никогда не дробите обновление токена на несколько PR — это открывает дверь несогласованностям.
Сколько времени должен занимать визуальный тест в пайплайне монорепозитория?
При нацеливании только на затронутые проекты: менее 10 минут для стандартного PR, затрагивающего один проект. Менее 20 минут для изменения в общем пакете, затрагивающем 3–5 проектов. Если ваши тесты занимают более 30 минут на PR, ваша стратегия фильтрации недостаточна. Полный слой (nightly) может занимать больше — 30–60 минут приемлемо.
Нужны ли отдельные baseline для каждого окружения (dev, staging, prod)?
Да, если окружения имеют разный контент или конфигурации. Нет, если рендеринг идентичен. На практике поддерживайте один baseline на проект, захваченный в стабильном staging-окружении. Сравнение с production заманчиво, но динамический контент (даты, данные пользователей, A/B-тесты) генерирует слишком много ложных срабатываний.
Работает ли визуальное тестирование в монорепозитории с Lerna?
Lerna — преимущественно инструмент управления версиями и публикации пакетов. Её граф зависимостей менее продвинут, чем у Nx или Turborepo. Использовать можно, но вероятно придётся дополнить кастомным скриптом для определения затронутых проектов. Если начинаете новый монорепозиторий, выбирайте Nx или Turborepo.
Как предотвратить игнорирование разработчиками результатов визуального тестирования?
Три правила. Поддерживайте уровень ложных срабатываний близким к нулю — если разработчики регулярно видят ложные срабатывания, они перестают смотреть. Блокируйте PR при сбоях визуальных тестов — без merge без подтверждения. И держите время пайплайна коротким — если визуальное тестирование добавляет 45 минут к PR, разработчики найдут способы его обойти.
Для углубления
- Визуальное тестирование и динамический контент: как тестировать, когда всё меняется при каждой загрузке
- Визуальное тестирование и lazy loading: как тестировать страницы, которые строятся при скролле
Монорепозиторий — отличная архитектура для совместного использования кода и согласованности. Но без адаптированной стратегии визуального тестирования он превращает каждое изменение в лотерею: сломал ли этот коммит что-то в проекте, о котором я даже не знаю? Ответ на этот вопрос никогда не должен быть «не знаю». Он должен быть автоматическим, быстрым и надёжным.