[{"data":1,"prerenderedAt":51},["ShallowReactive",2],{"wiki-test-automation-automated-tests-in-ci-ru":3},{"meta":4,"html":8,"headings":9,"words":50},{"title":5,"description":6,"published":7,"updated":7,"sourceUpdated":7},"Автотесты в CI","Автотесты в CI: события запуска пайплайна, этапы по скорости, параллельность и шардирование, отчёты JUnit XML, повторы, артефакты, результаты в прогонах.","2026-11-03","\u003Cp>\u003Cstrong>Непрерывная интеграция\u003C/strong> (continuous integration, CI) - практика разработки, при которой участники команды вливают свои изменения в общую основную ветку не реже раза в день, а каждую интеграцию проверяет автоматическая сборка с тестами. Автотесты - та часть сборки, которая решает, можно ли принять изменение. CI-система запускает их по событиям в репозитории кода и по расписанию, собирает результаты и сообщает их обратно в изменение, команде и другим системам.\u003C/p>\n\u003Cp>Непрерывную интеграцию как практику разработал Кент Бек в рамках экстремального программирования (Extreme Programming) в 1990-х годах. Мартин Фаулер опубликовал статью «Continuous Integration» в 2000 году и переработал её в январе 2024 года; в ней перечислены практики, среди них самотестирующаяся сборка, сборка при каждой отправке изменений в основную ветку и быстрая сборка. Сам термин иногда приписывают Гради Бучу, который, по словам Фаулера, лишь упомянул его мимоходом в книге об объектно-ориентированном проектировании. Программа ISTQB Advanced Level Test Automation Engineering (версия 2.0, 2024 год) посвящает отдельную главу встраиванию автотестов в пайплайны CI/CD: какие уровни тестирования выполняются на каком этапе и какие из них служат шлюзами качества.\u003C/p>\n\u003Ch2 id=\"какую-задачу-решает-ci\" class=\"wiki-heading\">Какую задачу решает CI\u003Cbutton type=\"button\" class=\"wiki-heading__anchor\" data-wiki-anchor=\"какую-задачу-решает-ci\" aria-label=\"Скопировать ссылку на раздел\" data-copied-label=\"Ссылка на раздел скопирована\">\u003Csvg class=\"wiki-heading__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M117.18,188.74a12,12,0,0,1,0,17l-5.12,5.12A58.26,58.26,0,0,1,70.6,228h0A58.62,58.62,0,0,1,29.14,127.92L63.89,93.17a58.64,58.64,0,0,1,98.56,28.11,12,12,0,1,1-23.37,5.44,34.65,34.65,0,0,0-58.22-16.58L46.11,144.89A34.62,34.62,0,0,0,70.57,204h0a34.41,34.41,0,0,0,24.49-10.14l5.11-5.12A12,12,0,0,1,117.18,188.74ZM226.83,45.17a58.65,58.65,0,0,0-82.93,0l-5.11,5.11a12,12,0,0,0,17,17l5.12-5.12a34.63,34.63,0,1,1,49,49L175.1,145.86A34.39,34.39,0,0,1,150.61,156h0a34.63,34.63,0,0,1-33.69-26.72,12,12,0,0,0-23.38,5.44A58.64,58.64,0,0,0,150.56,180h.05a58.28,58.28,0,0,0,41.47-17.17l34.75-34.75a58.62,58.62,0,0,0,0-82.91Z\"/>\u003C/svg>\u003Csvg class=\"wiki-heading__icon wiki-heading__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003C/button>\u003C/h2>\n\u003Cp>Без CI изменения интегрируют редко и крупными порциями. Конфликты и \u003Cspan class=\"wiki-term\" tabindex=\"0\" data-wiki-term=\"defect\">дефекты\u003C/span> накапливаются, а их причину трудно найти, потому что с последнего рабочего состояния сделано много изменений. Тесты, которые запускают вручную перед релизом, выполняются поздно, на неизвестном сочетании изменений, а иногда не выполняются вовсе.\u003C/p>\n\u003Cp>CI сокращает расстояние между изменением и его проверкой. Каждая отправка изменений или запрос на слияние запускает сборку и тесты, и автор получает результат, пока изменение ещё свежее. Требование к скорости Фаулер формулирует через правило экстремального программирования о десятиминутной сборке. Упавшую сборку исправляют сразу, чтобы основная ветка оставалась в рабочем состоянии.\u003C/p>\n\u003Cp>Уровни автотестов (модульные, интеграционные, сквозные) и их стоимость описаны в статье \u003Ca href=\"/ru/wiki/test-automation/automation-levels-and-test-pyramid\" class=\"wiki-link wiki-link--article\">Уровни автоматизации и пирамида тестов\u003C/a>. Здесь описано, как эти тесты запускаются в CI и как используются их результаты.\u003C/p>\n\u003Ch2 id=\"события-запуска-пайплайна\" class=\"wiki-heading\">События запуска пайплайна\u003Cbutton type=\"button\" class=\"wiki-heading__anchor\" data-wiki-anchor=\"события-запуска-пайплайна\" aria-label=\"Скопировать ссылку на раздел\" data-copied-label=\"Ссылка на раздел скопирована\">\u003Csvg class=\"wiki-heading__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M117.18,188.74a12,12,0,0,1,0,17l-5.12,5.12A58.26,58.26,0,0,1,70.6,228h0A58.62,58.62,0,0,1,29.14,127.92L63.89,93.17a58.64,58.64,0,0,1,98.56,28.11,12,12,0,1,1-23.37,5.44,34.65,34.65,0,0,0-58.22-16.58L46.11,144.89A34.62,34.62,0,0,0,70.57,204h0a34.41,34.41,0,0,0,24.49-10.14l5.11-5.12A12,12,0,0,1,117.18,188.74ZM226.83,45.17a58.65,58.65,0,0,0-82.93,0l-5.11,5.11a12,12,0,0,0,17,17l5.12-5.12a34.63,34.63,0,1,1,49,49L175.1,145.86A34.39,34.39,0,0,1,150.61,156h0a34.63,34.63,0,0,1-33.69-26.72,12,12,0,0,0-23.38,5.44A58.64,58.64,0,0,0,150.56,180h.05a58.28,58.28,0,0,0,41.47-17.17l34.75-34.75a58.62,58.62,0,0,0,0-82.91Z\"/>\u003C/svg>\u003Csvg class=\"wiki-heading__icon wiki-heading__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003C/button>\u003C/h2>\n\u003Cp>\u003Cstrong>Пайплайн\u003C/strong> (pipeline) - последовательность заданий, которые CI-система выполняет для одного события: сборка, запуск тестов, публикация артефактов, развёртывание. Пайплайн запускается по событию (trigger). Обычные события и тесты, которые по ним выполняются:\u003C/p>\n\u003Cdiv class=\"wiki-table wiki-table--cols-3\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Событие\u003C/th>\n\u003Cth>Когда срабатывает\u003C/th>\n\u003Cth>Типичный набор тестов\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>Отправка изменений (push)\u003C/td>\n\u003Ctd>В ветку отправлен коммит\u003C/td>\n\u003Ctd>Быстрые тесты: модульные тесты, статические проверки\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Запрос на слияние (pull или merge request)\u003C/td>\n\u003Ctd>Запрос открыт или обновлён\u003C/td>\n\u003Ctd>Тесты, нужные для принятия изменения\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Расписание\u003C/td>\n\u003Ctd>В заданное время, например каждую ночь\u003C/td>\n\u003Ctd>Полная регрессия, долгие и нефункциональные тесты\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Ручной запуск\u003C/td>\n\u003Ctd>Человек запускает пайплайн\u003C/td>\n\u003Ctd>Выбранный набор в выбранном окружении\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\u003C/div>\n\u003Cp>В GitHub Actions события объявляются в ключе \u003Ccode>on\u003C/code> рабочего процесса (workflow):\u003C/p>\n\u003Cdiv class=\"wiki-code\" data-language=\"yaml\">\u003Cdiv class=\"wiki-code__header\">\u003Cspan class=\"wiki-code__language\">yaml\u003C/span>\u003Cbutton type=\"button\" class=\"wiki-code__copy\" data-wiki-copy data-copied-label=\"Скопировано\">\u003Csvg class=\"wiki-code__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M216,28H88A12,12,0,0,0,76,40V76H40A12,12,0,0,0,28,88V216a12,12,0,0,0,12,12H168a12,12,0,0,0,12-12V180h36a12,12,0,0,0,12-12V40A12,12,0,0,0,216,28ZM156,204H52V100H156Zm48-48H180V88a12,12,0,0,0-12-12H100V52H204Z\"/>\u003C/svg>\u003Csvg class=\"wiki-code__icon wiki-code__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003Cspan class=\"wiki-code__label\">Копировать\u003C/span>\u003Cspan class=\"wiki-code__label wiki-code__label--done\">Скопировано\u003C/span>\u003C/button>\u003C/div>\u003Cpre>\u003Ccode>name: Tests\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n  schedule:\n    - cron: '0 2 * * *'\n  workflow_dispatch:\n    inputs:\n      suite:\n        description: 'Test suite to run'\n        required: true\n        default: 'smoke'\n        type: choice\n        options:\n        - smoke\n        - regression\n\njobs:\n  unit:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v7\n    - uses: actions/setup-node@v7\n      with:\n        node-version: lts/*\n    - run: npm ci\n    - run: npm test\u003C/code>\u003C/pre>\u003C/div>\n\u003Cp>Документация GitHub Actions задаёт подробности каждого события. По умолчанию \u003Ccode>pull_request\u003C/code> срабатывает на типы действий \u003Ccode>opened\u003C/code>, \u003Ccode>synchronize\u003C/code> и \u003Ccode>reopened\u003C/code>, а рабочий процесс выполняется на коммите слияния запроса (\u003Ccode>refs/pull/&lt;number&gt;/merge\u003C/code>), поэтому тесты проверяют результат слияния, а не только ветку. \u003Ccode>schedule\u003C/code> использует синтаксис cron POSIX, по умолчанию работает в UTC на последнем коммите ветки по умолчанию, не может запускаться чаще раза в пять минут и может задерживаться при высокой нагрузке. В публичных репозиториях запуски по расписанию отключаются после 60 дней без активности. \u003Ccode>workflow_dispatch\u003C/code> работает, только если файл рабочего процесса лежит в ветке по умолчанию; его входные параметры могут иметь типы \u003Ccode>boolean\u003C/code>, \u003Ccode>choice\u003C/code>, \u003Ccode>number\u003C/code>, \u003Ccode>environment\u003C/code> и \u003Ccode>string\u003C/code>.\u003C/p>\n\u003Ch2 id=\"этапы-по-скорости\" class=\"wiki-heading\">Этапы по скорости\u003Cbutton type=\"button\" class=\"wiki-heading__anchor\" data-wiki-anchor=\"этапы-по-скорости\" aria-label=\"Скопировать ссылку на раздел\" data-copied-label=\"Ссылка на раздел скопирована\">\u003Csvg class=\"wiki-heading__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M117.18,188.74a12,12,0,0,1,0,17l-5.12,5.12A58.26,58.26,0,0,1,70.6,228h0A58.62,58.62,0,0,1,29.14,127.92L63.89,93.17a58.64,58.64,0,0,1,98.56,28.11,12,12,0,1,1-23.37,5.44,34.65,34.65,0,0,0-58.22-16.58L46.11,144.89A34.62,34.62,0,0,0,70.57,204h0a34.41,34.41,0,0,0,24.49-10.14l5.11-5.12A12,12,0,0,1,117.18,188.74ZM226.83,45.17a58.65,58.65,0,0,0-82.93,0l-5.11,5.11a12,12,0,0,0,17,17l5.12-5.12a34.63,34.63,0,1,1,49,49L175.1,145.86A34.39,34.39,0,0,1,150.61,156h0a34.63,34.63,0,0,1-33.69-26.72,12,12,0,0,0-23.38,5.44A58.64,58.64,0,0,0,150.56,180h.05a58.28,58.28,0,0,0,41.47-17.17l34.75-34.75a58.62,58.62,0,0,0,0-82.91Z\"/>\u003C/svg>\u003Csvg class=\"wiki-heading__icon wiki-heading__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003C/button>\u003C/h2>\n\u003Cp>Тесты в пайплайне упорядочены от быстрых к медленным. Быстрый этап падает рано и не тратит ресурсы медленных этапов на изменение, которое уже сломано. Фаулер описывает это как конвейер развёртывания (deployment pipeline): сборка коммита (commit build) выполняется при каждой отправке изменений и должна быть быстрой, а вторичные сборки запускают более медленные тесты, например с настоящей базой данных, не блокируя каждый коммит.\u003C/p>\n\u003Cp>Программа CTAL-TAE распределяет уровни тестирования по этапам:\u003C/p>\n\u003Cul>\n\u003Cli>компонентные (модульные) тесты выполняются на этапе сборки и служат шлюзами качества пайплайна; тесты интеграции компонентов выполняются вместе с ними;\u003C/li>\n\u003Cli>тесты системной интеграции часто служат шлюзами качества пайплайна непрерывной поставки;\u003C/li>\n\u003Cli>системные тесты могут выполняться в пайплайне непрерывного развёртывания как последний шлюз качества перед поставкой продукта;\u003C/li>\n\u003Cli>системные и приёмочные тесты выполняются либо на этапе развёртывания, где падение может откатить развёртывание, либо в отдельном пайплайне, который запускается успешным развёртыванием.\u003C/li>\n\u003C/ul>\n\u003Cp>Типичный порядок с условными длительностями:\u003C/p>\n\u003Cdiv class=\"wiki-code wiki-code--plain\">\u003Cpre>\u003Ccode>push / pull request\n |\n +-- build, static checks               1-2 min\n +-- unit tests                         2-5 min    quality gate\n +-- integration tests                  5-10 min   quality gate\n +-- deploy to test environment\n +-- end-to-end tests, 4 shards         10-15 min  quality gate\n +-- report: JUnit XML, artifacts\nnightly schedule\n +-- full regression, performance tests\u003C/code>\u003C/pre>\u003C/div>\n\u003Cp>В GitHub Actions порядок заданий задаётся через \u003Ccode>needs\u003C/code>: задание начинается только после успешного завершения заданий, от которых оно зависит, а если одно из них упало или пропущено, зависимые задания пропускаются.\u003C/p>\n\u003Ch2 id=\"параллельность-и-шардирование\" class=\"wiki-heading\">Параллельность и шардирование\u003Cbutton type=\"button\" class=\"wiki-heading__anchor\" data-wiki-anchor=\"параллельность-и-шардирование\" aria-label=\"Скопировать ссылку на раздел\" data-copied-label=\"Ссылка на раздел скопирована\">\u003Csvg class=\"wiki-heading__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M117.18,188.74a12,12,0,0,1,0,17l-5.12,5.12A58.26,58.26,0,0,1,70.6,228h0A58.62,58.62,0,0,1,29.14,127.92L63.89,93.17a58.64,58.64,0,0,1,98.56,28.11,12,12,0,1,1-23.37,5.44,34.65,34.65,0,0,0-58.22-16.58L46.11,144.89A34.62,34.62,0,0,0,70.57,204h0a34.41,34.41,0,0,0,24.49-10.14l5.11-5.12A12,12,0,0,1,117.18,188.74ZM226.83,45.17a58.65,58.65,0,0,0-82.93,0l-5.11,5.11a12,12,0,0,0,17,17l5.12-5.12a34.63,34.63,0,1,1,49,49L175.1,145.86A34.39,34.39,0,0,1,150.61,156h0a34.63,34.63,0,0,1-33.69-26.72,12,12,0,0,0-23.38,5.44A58.64,58.64,0,0,0,150.56,180h.05a58.28,58.28,0,0,0,41.47-17.17l34.75-34.75a58.62,58.62,0,0,0,0-82.91Z\"/>\u003C/svg>\u003Csvg class=\"wiki-heading__icon wiki-heading__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003C/button>\u003C/h2>\n\u003Cp>Параллельность внутри одной машины и распределение между машинами - два разных уровня. Playwright запускает файлы тестов в параллельных рабочих процессах (workers); по умолчанию их число - половина логических ядер процессора. Его руководство по CI рекомендует один рабочий процесс на агентах CI ради стабильности и воспроизводимости, а для более широкого распараллеливания - шардирование.\u003C/p>\n\u003Cp>\u003Cstrong>Шардирование\u003C/strong> (sharding) - разбиение \u003Cspan class=\"wiki-term\" tabindex=\"0\" data-wiki-term=\"test-suite\">набора тестов\u003C/span> на части, которые выполняются как независимые задания на разных машинах. В Playwright опция \u003Ccode>--shard=x/y\u003C/code> выбирает часть x из y. С \u003Ccode>fullyParallel: true\u003C/code> части балансируются по отдельным тестам; без неё целые файлы распределяются по шардам, и файлы разного размера дают шарды разной длительности. В GitHub Actions шарды создаются матрицей:\u003C/p>\n\u003Cdiv class=\"wiki-code\" data-language=\"yaml\">\u003Cdiv class=\"wiki-code__header\">\u003Cspan class=\"wiki-code__language\">yaml\u003C/span>\u003Cbutton type=\"button\" class=\"wiki-code__copy\" data-wiki-copy data-copied-label=\"Скопировано\">\u003Csvg class=\"wiki-code__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M216,28H88A12,12,0,0,0,76,40V76H40A12,12,0,0,0,28,88V216a12,12,0,0,0,12,12H168a12,12,0,0,0,12-12V180h36a12,12,0,0,0,12-12V40A12,12,0,0,0,216,28ZM156,204H52V100H156Zm48-48H180V88a12,12,0,0,0-12-12H100V52H204Z\"/>\u003C/svg>\u003Csvg class=\"wiki-code__icon wiki-code__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003Cspan class=\"wiki-code__label\">Копировать\u003C/span>\u003Cspan class=\"wiki-code__label wiki-code__label--done\">Скопировано\u003C/span>\u003C/button>\u003C/div>\u003Cpre>\u003Ccode>jobs:\n  e2e:\n    needs: unit\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        shardIndex: [1, 2, 3, 4]\n        shardTotal: [4]\n    steps:\n    - uses: actions/checkout@v7\n    - uses: actions/setup-node@v7\n      with:\n        node-version: lts/*\n    - run: npm ci\n    - run: npx playwright install --with-deps\n    - run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}\u003C/code>\u003C/pre>\u003C/div>\n\u003Cp>Матрица порождает не больше 256 заданий на один запуск рабочего процесса. Опция \u003Ccode>fail-fast\u003C/code> по умолчанию равна \u003Ccode>true\u003C/code>: когда одно задание матрицы падает, остальные отменяются. Для шардов тестов её ставят в \u003Ccode>false\u003C/code>, чтобы каждый шард доработал и сообщил обо всех падениях. В GitLab CI то же делается ключевым словом \u003Ccode>parallel\u003C/code> и переменными \u003Ccode>CI_NODE_INDEX\u003C/code> и \u003Ccode>CI_NODE_TOTAL\u003C/code>.\u003C/p>\n\u003Cp>Каждый шард создаёт свой отчёт. Playwright объединяет их командой \u003Ccode>npx playwright merge-reports\u003C/code> из blob-отчётов; для \u003Cspan class=\"wiki-term\" tabindex=\"0\" data-wiki-term=\"junit-xml\">JUnit XML\u003C/span> каждый шард выгружает свой файл, а объединяет их CI-система или принимающая система.\u003C/p>\n\u003Ch2 id=\"отчёты-о-тестах\" class=\"wiki-heading\">Отчёты о тестах\u003Cbutton type=\"button\" class=\"wiki-heading__anchor\" data-wiki-anchor=\"отчёты-о-тестах\" aria-label=\"Скопировать ссылку на раздел\" data-copied-label=\"Ссылка на раздел скопирована\">\u003Csvg class=\"wiki-heading__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M117.18,188.74a12,12,0,0,1,0,17l-5.12,5.12A58.26,58.26,0,0,1,70.6,228h0A58.62,58.62,0,0,1,29.14,127.92L63.89,93.17a58.64,58.64,0,0,1,98.56,28.11,12,12,0,1,1-23.37,5.44,34.65,34.65,0,0,0-58.22-16.58L46.11,144.89A34.62,34.62,0,0,0,70.57,204h0a34.41,34.41,0,0,0,24.49-10.14l5.11-5.12A12,12,0,0,1,117.18,188.74ZM226.83,45.17a58.65,58.65,0,0,0-82.93,0l-5.11,5.11a12,12,0,0,0,17,17l5.12-5.12a34.63,34.63,0,1,1,49,49L175.1,145.86A34.39,34.39,0,0,1,150.61,156h0a34.63,34.63,0,0,1-33.69-26.72,12,12,0,0,0-23.38,5.44A58.64,58.64,0,0,0,150.56,180h.05a58.28,58.28,0,0,0,41.47-17.17l34.75-34.75a58.62,58.62,0,0,0,0-82.91Z\"/>\u003C/svg>\u003Csvg class=\"wiki-heading__icon wiki-heading__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003C/button>\u003C/h2>\n\u003Cp>CI-система читает \u003Cspan class=\"wiki-term\" tabindex=\"0\" data-wiki-term=\"test-result\">результаты тестов\u003C/span> из файлов отчётов. Фактически стандартный формат обмена - JUnit XML; его структура описана в статье \u003Ca href=\"/ru/wiki/test-management/what-is-a-test-management-system\" class=\"wiki-link wiki-link--article\">Что такое система управления тестированием\u003C/a>. Тестовые фреймворки записывают его по опции или через генератор отчётов (reporter). В Playwright:\u003C/p>\n\u003Cdiv class=\"wiki-code\" data-language=\"typescript\">\u003Cdiv class=\"wiki-code__header\">\u003Cspan class=\"wiki-code__language\">typescript\u003C/span>\u003Cbutton type=\"button\" class=\"wiki-code__copy\" data-wiki-copy data-copied-label=\"Скопировано\">\u003Csvg class=\"wiki-code__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M216,28H88A12,12,0,0,0,76,40V76H40A12,12,0,0,0,28,88V216a12,12,0,0,0,12,12H168a12,12,0,0,0,12-12V180h36a12,12,0,0,0,12-12V40A12,12,0,0,0,216,28ZM156,204H52V100H156Zm48-48H180V88a12,12,0,0,0-12-12H100V52H204Z\"/>\u003C/svg>\u003Csvg class=\"wiki-code__icon wiki-code__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003Cspan class=\"wiki-code__label\">Копировать\u003C/span>\u003Cspan class=\"wiki-code__label wiki-code__label--done\">Скопировано\u003C/span>\u003C/button>\u003C/div>\u003Cpre>\u003Ccode>import { defineConfig } from '@playwright/test';\n\nexport default defineConfig({\n  forbidOnly: !!process.env.CI,\n  retries: process.env.CI ? 2 : 0,\n  reporter: process.env.CI\n    ? [['junit', { outputFile: 'results.xml' }], ['blob']]\n    : 'html',\n  use: {\n    trace: 'on-first-retry',\n    screenshot: 'only-on-failure',\n  },\n});\u003C/code>\u003C/pre>\u003C/div>\n\u003Cp>Опция \u003Ccode>forbidOnly\u003C/code> делает прогон неуспешным, если по ошибке закоммичен тест с пометкой \u003Ccode>test.only\u003C/code>. В pytest отчёт создаётся командой \u003Ccode>pytest --junit-xml=results.xml\u003C/code>.\u003C/p>\n\u003Cp>GitLab CI читает JUnit XML из ключа \u003Ccode>artifacts:reports:junit\u003C/code>:\u003C/p>\n\u003Cdiv class=\"wiki-code\" data-language=\"yaml\">\u003Cdiv class=\"wiki-code__header\">\u003Cspan class=\"wiki-code__language\">yaml\u003C/span>\u003Cbutton type=\"button\" class=\"wiki-code__copy\" data-wiki-copy data-copied-label=\"Скопировано\">\u003Csvg class=\"wiki-code__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M216,28H88A12,12,0,0,0,76,40V76H40A12,12,0,0,0,28,88V216a12,12,0,0,0,12,12H168a12,12,0,0,0,12-12V180h36a12,12,0,0,0,12-12V40A12,12,0,0,0,216,28ZM156,204H52V100H156Zm48-48H180V88a12,12,0,0,0-12-12H100V52H204Z\"/>\u003C/svg>\u003Csvg class=\"wiki-code__icon wiki-code__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003Cspan class=\"wiki-code__label\">Копировать\u003C/span>\u003Cspan class=\"wiki-code__label wiki-code__label--done\">Скопировано\u003C/span>\u003C/button>\u003C/div>\u003Cpre>\u003Ccode>e2e:\n  stage: test\n  image: mcr.microsoft.com/playwright:v1.64.1-noble\n  parallel: 4\n  script:\n    - npm ci\n    - npx playwright test --shard=$CI_NODE_INDEX/$CI_NODE_TOTAL\n  artifacts:\n    when: always\n    paths:\n      - results.xml\n      - test-results/\n    reports:\n      junit: results.xml\u003C/code>\u003C/pre>\u003C/div>\n\u003Cp>\u003Ccode>when: always\u003C/code> выгружает отчёт и тогда, когда задание упало, то есть именно тогда, когда он нужен. GitLab показывает результаты на вкладке «Tests» пайплайна и в сводке тестов запроса на слияние. В запросе на слияние он сравнивает результаты с целевой веткой и выделяет новые падения, существующие падения и исправленные падения. Документация задаёт ограничения: один файл JUnit должен быть меньше 30 МБ, все файлы задания - меньше 100 МБ, а из тестов с одинаковыми именами используется только первый.\u003C/p>\n\u003Ch2 id=\"повторы-упавших-тестов\" class=\"wiki-heading\">Повторы упавших тестов\u003Cbutton type=\"button\" class=\"wiki-heading__anchor\" data-wiki-anchor=\"повторы-упавших-тестов\" aria-label=\"Скопировать ссылку на раздел\" data-copied-label=\"Ссылка на раздел скопирована\">\u003Csvg class=\"wiki-heading__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M117.18,188.74a12,12,0,0,1,0,17l-5.12,5.12A58.26,58.26,0,0,1,70.6,228h0A58.62,58.62,0,0,1,29.14,127.92L63.89,93.17a58.64,58.64,0,0,1,98.56,28.11,12,12,0,1,1-23.37,5.44,34.65,34.65,0,0,0-58.22-16.58L46.11,144.89A34.62,34.62,0,0,0,70.57,204h0a34.41,34.41,0,0,0,24.49-10.14l5.11-5.12A12,12,0,0,1,117.18,188.74ZM226.83,45.17a58.65,58.65,0,0,0-82.93,0l-5.11,5.11a12,12,0,0,0,17,17l5.12-5.12a34.63,34.63,0,1,1,49,49L175.1,145.86A34.39,34.39,0,0,1,150.61,156h0a34.63,34.63,0,0,1-33.69-26.72,12,12,0,0,0-23.38,5.44A58.64,58.64,0,0,0,150.56,180h.05a58.28,58.28,0,0,0,41.47-17.17l34.75-34.75a58.62,58.62,0,0,0,0-82.91Z\"/>\u003C/svg>\u003Csvg class=\"wiki-heading__icon wiki-heading__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003C/button>\u003C/h2>\n\u003Cp>Пайплайн может автоматически перезапускать упавшие тесты: в Playwright опцией \u003Ccode>retries\u003C/code>, в pytest плагином \u003Ccode>pytest-rerunfailures\u003C/code>, в Maven свойством \u003Ccode>surefire.rerunFailingTestsCount\u003C/code>. Тест, который прошёл при повторе, отмечается как нестабильный (flaky). Повторы обычно включают только в CI, как в конфигурации выше, с небольшим числом попыток.\u003C/p>\n\u003Cp>Повтор превращает плавающее падение в прохождение, в том числе падение, вызванное настоящим дефектом. Поэтому статус flaky сохраняется в отчёте, а не сливается с passed. Maven Surefire записывает неуспешные попытки как элементы \u003Ccode>flakyFailure\u003C/code> внутри элемента \u003Ccode>testcase\u003C/code>; Playwright может сделать прогон неуспешным при любом \u003Cspan class=\"wiki-term\" tabindex=\"0\" data-wiki-term=\"flaky-test\">нестабильном тесте\u003C/span> с опцией \u003Ccode>--fail-on-flaky-tests\u003C/code>. Причины, обнаружение и \u003Cspan class=\"wiki-term\" tabindex=\"0\" data-wiki-term=\"test-quarantine\">карантин\u003C/span> нестабильных тестов описаны в статье \u003Ca href=\"/ru/wiki/test-automation/flaky-tests\" class=\"wiki-link wiki-link--article\">Нестабильные (flaky) тесты\u003C/a>.\u003C/p>\n\u003Ch2 id=\"артефакты\" class=\"wiki-heading\">Артефакты\u003Cbutton type=\"button\" class=\"wiki-heading__anchor\" data-wiki-anchor=\"артефакты\" aria-label=\"Скопировать ссылку на раздел\" data-copied-label=\"Ссылка на раздел скопирована\">\u003Csvg class=\"wiki-heading__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M117.18,188.74a12,12,0,0,1,0,17l-5.12,5.12A58.26,58.26,0,0,1,70.6,228h0A58.62,58.62,0,0,1,29.14,127.92L63.89,93.17a58.64,58.64,0,0,1,98.56,28.11,12,12,0,1,1-23.37,5.44,34.65,34.65,0,0,0-58.22-16.58L46.11,144.89A34.62,34.62,0,0,0,70.57,204h0a34.41,34.41,0,0,0,24.49-10.14l5.11-5.12A12,12,0,0,1,117.18,188.74ZM226.83,45.17a58.65,58.65,0,0,0-82.93,0l-5.11,5.11a12,12,0,0,0,17,17l5.12-5.12a34.63,34.63,0,1,1,49,49L175.1,145.86A34.39,34.39,0,0,1,150.61,156h0a34.63,34.63,0,0,1-33.69-26.72,12,12,0,0,0-23.38,5.44A58.64,58.64,0,0,0,150.56,180h.05a58.28,58.28,0,0,0,41.47-17.17l34.75-34.75a58.62,58.62,0,0,0,0-82.91Z\"/>\u003C/svg>\u003Csvg class=\"wiki-heading__icon wiki-heading__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003C/button>\u003C/h2>\n\u003Cp>Упавший тест на агенте CI нельзя отладить интерактивно: после окончания задания агента уже нет. Поэтому задание сохраняет \u003Cstrong>артефакты\u003C/strong> (artifacts) - файлы, которые документируют запуск:\u003C/p>\n\u003Cul>\n\u003Cli>снимки экрана в момент падения;\u003C/li>\n\u003Cli>трассировки с действиями, сетевыми запросами и снимками DOM (в Playwright \u003Ccode>trace: 'on-first-retry'\u003C/code> записывает трассировку при первом повторе);\u003C/li>\n\u003Cli>видео теста;\u003C/li>\n\u003Cli>журналы приложения и \u003Cspan class=\"wiki-term\" tabindex=\"0\" data-wiki-term=\"test-environment\">тестового окружения\u003C/span>;\u003C/li>\n\u003Cli>сами файлы отчётов.\u003C/li>\n\u003C/ul>\n\u003Cp>В GitHub Actions артефакты выгружаются действием \u003Ccode>actions/upload-artifact\u003C/code>:\u003C/p>\n\u003Cdiv class=\"wiki-code\" data-language=\"yaml\">\u003Cdiv class=\"wiki-code__header\">\u003Cspan class=\"wiki-code__language\">yaml\u003C/span>\u003Cbutton type=\"button\" class=\"wiki-code__copy\" data-wiki-copy data-copied-label=\"Скопировано\">\u003Csvg class=\"wiki-code__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M216,28H88A12,12,0,0,0,76,40V76H40A12,12,0,0,0,28,88V216a12,12,0,0,0,12,12H168a12,12,0,0,0,12-12V180h36a12,12,0,0,0,12-12V40A12,12,0,0,0,216,28ZM156,204H52V100H156Zm48-48H180V88a12,12,0,0,0-12-12H100V52H204Z\"/>\u003C/svg>\u003Csvg class=\"wiki-code__icon wiki-code__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003Cspan class=\"wiki-code__label\">Копировать\u003C/span>\u003Cspan class=\"wiki-code__label wiki-code__label--done\">Скопировано\u003C/span>\u003C/button>\u003C/div>\u003Cpre>\u003Ccode>    - uses: actions/upload-artifact@v7\n      if: ${{ !cancelled() }}\n      with:\n        name: results-${{ matrix.shardIndex }}\n        path: |\n          results.xml\n          test-results/\n        retention-days: 7\u003C/code>\u003C/pre>\u003C/div>\n\u003Cp>Условие \u003Ccode>${{ !cancelled() }}\u003C/code> выгружает артефакты и после упавших шагов. Срок хранения ограничивает объём: артефакты падения нужны днями, а не месяцами. GitLab умеет связать файл с упавшим тестом: строка \u003Ccode>[[ATTACHMENT|/path/to/file]]\u003C/code> в \u003Ccode>system-out\u003C/code> \u003Cspan class=\"wiki-term\" tabindex=\"0\" data-wiki-term=\"test-case\">тест-кейса\u003C/span> в JUnit XML появляется ссылкой в подробностях теста.\u003C/p>\n\u003Ch2 id=\"результаты-в-тестовых-прогонах-системы-управления-тестированием\" class=\"wiki-heading\">Результаты в тестовых прогонах системы управления тестированием\u003Cbutton type=\"button\" class=\"wiki-heading__anchor\" data-wiki-anchor=\"результаты-в-тестовых-прогонах-системы-управления-тестированием\" aria-label=\"Скопировать ссылку на раздел\" data-copied-label=\"Ссылка на раздел скопирована\">\u003Csvg class=\"wiki-heading__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M117.18,188.74a12,12,0,0,1,0,17l-5.12,5.12A58.26,58.26,0,0,1,70.6,228h0A58.62,58.62,0,0,1,29.14,127.92L63.89,93.17a58.64,58.64,0,0,1,98.56,28.11,12,12,0,1,1-23.37,5.44,34.65,34.65,0,0,0-58.22-16.58L46.11,144.89A34.62,34.62,0,0,0,70.57,204h0a34.41,34.41,0,0,0,24.49-10.14l5.11-5.12A12,12,0,0,1,117.18,188.74ZM226.83,45.17a58.65,58.65,0,0,0-82.93,0l-5.11,5.11a12,12,0,0,0,17,17l5.12-5.12a34.63,34.63,0,1,1,49,49L175.1,145.86A34.39,34.39,0,0,1,150.61,156h0a34.63,34.63,0,0,1-33.69-26.72,12,12,0,0,0-23.38,5.44A58.64,58.64,0,0,0,150.56,180h.05a58.28,58.28,0,0,0,41.47-17.17l34.75-34.75a58.62,58.62,0,0,0,0-82.91Z\"/>\u003C/svg>\u003Csvg class=\"wiki-heading__icon wiki-heading__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003C/button>\u003C/h2>\n\u003Cp>CI-система показывает результаты одного пайплайна. \u003Cspan class=\"wiki-term\" tabindex=\"0\" data-wiki-term=\"test-management-system\">Система управления тестированием\u003C/span> хранит их как \u003Cspan class=\"wiki-term\" tabindex=\"0\" data-wiki-term=\"test-run\">тестовые прогоны\u003C/span> рядом с ручным тестированием и во времени. Обычное соответствие:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>один тестовый прогон на пайплайн\u003C/strong>: прогон называется по пайплайну и фиксирует коммит, ветку, номер сборки и окружение;\u003C/li>\n\u003Cli>\u003Cstrong>один результат на тест-кейс\u003C/strong>: каждый автотест связан с тест-кейсом идентификатором, который известен обеим сторонам;\u003C/li>\n\u003Cli>\u003Cstrong>статусы\u003C/strong>: \u003Ccode>failure\u003C/code>, \u003Ccode>error\u003C/code> и \u003Ccode>skipped\u003C/code> из JUnit XML сопоставляются со статусами прогона, а нестабильные результаты получают отдельную отметку;\u003C/li>\n\u003Cli>\u003Cstrong>вложения\u003C/strong>: снимки экрана и трассировки прикладываются к результатам упавших тестов.\u003C/li>\n\u003C/ul>\n\u003Cp>Идентификатор тест-кейса указывается в самом тесте. В Playwright это может быть аннотация:\u003C/p>\n\u003Cdiv class=\"wiki-code\" data-language=\"typescript\">\u003Cdiv class=\"wiki-code__header\">\u003Cspan class=\"wiki-code__language\">typescript\u003C/span>\u003Cbutton type=\"button\" class=\"wiki-code__copy\" data-wiki-copy data-copied-label=\"Скопировано\">\u003Csvg class=\"wiki-code__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M216,28H88A12,12,0,0,0,76,40V76H40A12,12,0,0,0,28,88V216a12,12,0,0,0,12,12H168a12,12,0,0,0,12-12V180h36a12,12,0,0,0,12-12V40A12,12,0,0,0,216,28ZM156,204H52V100H156Zm48-48H180V88a12,12,0,0,0-12-12H100V52H204Z\"/>\u003C/svg>\u003Csvg class=\"wiki-code__icon wiki-code__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003Cspan class=\"wiki-code__label\">Копировать\u003C/span>\u003Cspan class=\"wiki-code__label wiki-code__label--done\">Скопировано\u003C/span>\u003C/button>\u003C/div>\u003Cpre>\u003Ccode>import { test, expect } from '@playwright/test';\n\ntest('pay with saved card', {\n  annotation: { type: 'test-case-id', description: 'TC-12' },\n}, async ({ page }) =&gt; {\n  await page.goto('https://example.com/checkout');\n  await page.getByRole('button', { name: 'Pay with saved card' }).click();\n  await expect(page.getByRole('heading', { name: 'Payment accepted' })).toBeVisible();\n});\u003C/code>\u003C/pre>\u003C/div>\n\u003Cp>Генератор отчётов JUnit в Playwright записывает аннотации как свойства тест-кейса:\u003C/p>\n\u003Cdiv class=\"wiki-code\" data-language=\"xml\">\u003Cdiv class=\"wiki-code__header\">\u003Cspan class=\"wiki-code__language\">xml\u003C/span>\u003Cbutton type=\"button\" class=\"wiki-code__copy\" data-wiki-copy data-copied-label=\"Скопировано\">\u003Csvg class=\"wiki-code__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M216,28H88A12,12,0,0,0,76,40V76H40A12,12,0,0,0,28,88V216a12,12,0,0,0,12,12H168a12,12,0,0,0,12-12V180h36a12,12,0,0,0,12-12V40A12,12,0,0,0,216,28ZM156,204H52V100H156Zm48-48H180V88a12,12,0,0,0-12-12H100V52H204Z\"/>\u003C/svg>\u003Csvg class=\"wiki-code__icon wiki-code__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003Cspan class=\"wiki-code__label\">Копировать\u003C/span>\u003Cspan class=\"wiki-code__label wiki-code__label--done\">Скопировано\u003C/span>\u003C/button>\u003C/div>\u003Cpre>\u003Ccode>&lt;testcase name=&quot;pay with saved card&quot; classname=&quot;checkout.spec.ts&quot; time=&quot;1.62&quot;&gt;\n  &lt;properties&gt;\n    &lt;property name=&quot;test-case-id&quot; value=&quot;TC-12&quot;/&gt;\n  &lt;/properties&gt;\n&lt;/testcase&gt;\u003C/code>\u003C/pre>\u003C/div>\n\u003Cp>В pytest то же делает фикстура \u003Ccode>record_property\u003C/code>. Результаты попадают в систему управления тестированием либо как импортированный файл отчёта, либо через её API, который вызывает задание в конце пайплайна. Структура тестового прогона и статусы результатов описаны в статье \u003Ca href=\"/ru/wiki/test-management/test-run\" class=\"wiki-link wiki-link--article\">Тестовый прогон\u003C/a>.\u003C/p>\n\u003Ch2 id=\"выбор-тестов-и-анализ-влияния-изменений\" class=\"wiki-heading\">Выбор тестов и анализ влияния изменений\u003Cbutton type=\"button\" class=\"wiki-heading__anchor\" data-wiki-anchor=\"выбор-тестов-и-анализ-влияния-изменений\" aria-label=\"Скопировать ссылку на раздел\" data-copied-label=\"Ссылка на раздел скопирована\">\u003Csvg class=\"wiki-heading__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M117.18,188.74a12,12,0,0,1,0,17l-5.12,5.12A58.26,58.26,0,0,1,70.6,228h0A58.62,58.62,0,0,1,29.14,127.92L63.89,93.17a58.64,58.64,0,0,1,98.56,28.11,12,12,0,1,1-23.37,5.44,34.65,34.65,0,0,0-58.22-16.58L46.11,144.89A34.62,34.62,0,0,0,70.57,204h0a34.41,34.41,0,0,0,24.49-10.14l5.11-5.12A12,12,0,0,1,117.18,188.74ZM226.83,45.17a58.65,58.65,0,0,0-82.93,0l-5.11,5.11a12,12,0,0,0,17,17l5.12-5.12a34.63,34.63,0,1,1,49,49L175.1,145.86A34.39,34.39,0,0,1,150.61,156h0a34.63,34.63,0,0,1-33.69-26.72,12,12,0,0,0-23.38,5.44A58.64,58.64,0,0,0,150.56,180h.05a58.28,58.28,0,0,0,41.47-17.17l34.75-34.75a58.62,58.62,0,0,0,0-82.91Z\"/>\u003C/svg>\u003Csvg class=\"wiki-heading__icon wiki-heading__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003C/button>\u003C/h2>\n\u003Cp>Когда полный набор становится слишком долгим для каждого изменения, пайплайн запускает только тесты, которые затрагивает изменение. \u003Cstrong>Анализ влияния изменений на тесты\u003C/strong> (test impact analysis) определяет, какое подмножество тестов запускать для данного набора изменений. Пол Хаммант на сайте Фаулера (2017) описывает обычный механизм: карту соответствия тестов и исходных файлов, которые они выполняют. Карту строят по покрытию кода, запуская каждый тест отдельно, хранят в системе контроля версий и обновляют средствами CI.\u003C/p>\n\u003Cp>У карты есть пробелы. Изменения конфигурации, файлов ресурсов и данных не видны в покрытии кода, а новые тесты не попадают в карту, пока её не перестроят. Поэтому выбор тестов сочетают с полным запуском по расписанию. Более простая форма выбора встроена в некоторые средства запуска: опция Playwright \u003Ccode>--only-changed\u003C/code> запускает только файлы тестов, изменённые относительно ссылки Git.\u003C/p>\n\u003Ch2 id=\"окружения-и-тестовые-данные-в-ci\" class=\"wiki-heading\">Окружения и тестовые данные в CI\u003Cbutton type=\"button\" class=\"wiki-heading__anchor\" data-wiki-anchor=\"окружения-и-тестовые-данные-в-ci\" aria-label=\"Скопировать ссылку на раздел\" data-copied-label=\"Ссылка на раздел скопирована\">\u003Csvg class=\"wiki-heading__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M117.18,188.74a12,12,0,0,1,0,17l-5.12,5.12A58.26,58.26,0,0,1,70.6,228h0A58.62,58.62,0,0,1,29.14,127.92L63.89,93.17a58.64,58.64,0,0,1,98.56,28.11,12,12,0,1,1-23.37,5.44,34.65,34.65,0,0,0-58.22-16.58L46.11,144.89A34.62,34.62,0,0,0,70.57,204h0a34.41,34.41,0,0,0,24.49-10.14l5.11-5.12A12,12,0,0,1,117.18,188.74ZM226.83,45.17a58.65,58.65,0,0,0-82.93,0l-5.11,5.11a12,12,0,0,0,17,17l5.12-5.12a34.63,34.63,0,1,1,49,49L175.1,145.86A34.39,34.39,0,0,1,150.61,156h0a34.63,34.63,0,0,1-33.69-26.72,12,12,0,0,0-23.38,5.44A58.64,58.64,0,0,0,150.56,180h.05a58.28,58.28,0,0,0,41.47-17.17l34.75-34.75a58.62,58.62,0,0,0,0-82.91Z\"/>\u003C/svg>\u003Csvg class=\"wiki-heading__icon wiki-heading__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003C/button>\u003C/h2>\n\u003Cp>Тесты в CI выполняются в окружении, которое готовит пайплайн или которое существует постоянно. Фаулер относит тестирование в копии продуктивного окружения к практикам CI. Распространённые подходы:\u003C/p>\n\u003Cul>\n\u003Cli>зависимости (база данных, очередь сообщений, заглушки внешних сервисов) запускаются в контейнерах на время задания, и каждый пайплайн получает чистое состояние;\u003C/li>\n\u003Cli>развёрнутое тестовое окружение общее для пайплайнов, а запуски, которые его используют, ограничиваются группой параллельности; в GitHub Actions \u003Ccode>concurrency\u003C/code> с \u003Ccode>cancel-in-progress: true\u003C/code> отменяет более старый запуск в той же группе;\u003C/li>\n\u003Cli>тестовые данные создают сами тесты или загружает пайплайн перед тестами, и каждый шард использует свои данные, чтобы параллельные задания не мешали друг другу;\u003C/li>\n\u003Cli>секреты, например токены тестовых учётных записей, берутся из хранилища секретов CI-системы, а не из репозитория.\u003C/li>\n\u003C/ul>\n\u003Cp>Окружение записывается вместе с результатами: версия сборки, адрес окружения, браузер и его версия. Без этого упавший результат нельзя воспроизвести.\u003C/p>\n\u003Ch2 id=\"ci-непрерывная-поставка-и-непрерывное-развёртывание\" class=\"wiki-heading\">CI, непрерывная поставка и непрерывное развёртывание\u003Cbutton type=\"button\" class=\"wiki-heading__anchor\" data-wiki-anchor=\"ci-непрерывная-поставка-и-непрерывное-развёртывание\" aria-label=\"Скопировать ссылку на раздел\" data-copied-label=\"Ссылка на раздел скопирована\">\u003Csvg class=\"wiki-heading__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M117.18,188.74a12,12,0,0,1,0,17l-5.12,5.12A58.26,58.26,0,0,1,70.6,228h0A58.62,58.62,0,0,1,29.14,127.92L63.89,93.17a58.64,58.64,0,0,1,98.56,28.11,12,12,0,1,1-23.37,5.44,34.65,34.65,0,0,0-58.22-16.58L46.11,144.89A34.62,34.62,0,0,0,70.57,204h0a34.41,34.41,0,0,0,24.49-10.14l5.11-5.12A12,12,0,0,1,117.18,188.74ZM226.83,45.17a58.65,58.65,0,0,0-82.93,0l-5.11,5.11a12,12,0,0,0,17,17l5.12-5.12a34.63,34.63,0,1,1,49,49L175.1,145.86A34.39,34.39,0,0,1,150.61,156h0a34.63,34.63,0,0,1-33.69-26.72,12,12,0,0,0-23.38,5.44A58.64,58.64,0,0,0,150.56,180h.05a58.28,58.28,0,0,0,41.47-17.17l34.75-34.75a58.62,58.62,0,0,0,0-82.91Z\"/>\u003C/svg>\u003Csvg class=\"wiki-heading__icon wiki-heading__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003C/button>\u003C/h2>\n\u003Cp>Таблица сравнивает три связанные практики.\u003C/p>\n\u003Cdiv class=\"wiki-table wiki-table--cols-4\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003C/th>\n\u003Cth>Непрерывная интеграция\u003C/th>\n\u003Cth>Непрерывная поставка\u003C/th>\n\u003Cth>Непрерывное развёртывание\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>Цель\u003C/td>\n\u003Ctd>Изменения часто интегрируются и проверяются\u003C/td>\n\u003Ctd>Продукт можно выпустить в любой момент\u003C/td>\n\u003Ctd>Каждое изменение выпускается автоматически\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Конец пайплайна\u003C/td>\n\u003Ctd>Проверенная сборка\u003C/td>\n\u003Ctd>Кандидат в релиз, готовый к продакшену\u003C/td>\n\u003Ctd>Изменение в продакшене\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Решение о релизе\u003C/td>\n\u003Ctd>Не входит в CI\u003C/td>\n\u003Ctd>Принимают люди\u003C/td>\n\u003Ctd>Принимает пайплайн\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Роль автотестов\u003C/td>\n\u003Ctd>Проверить каждое изменение\u003C/td>\n\u003Ctd>Допустить кандидата в релиз\u003C/td>\n\u003Ctd>Единственный шлюз перед продакшеном\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\u003C/div>\n\u003Cp>Непрерывная поставка (continuous delivery) описана в книге Джеза Хамбла и Дэвида Фарли «Continuous Delivery» (2010). Фаулер определяет её как создание программного обеспечения так, чтобы его можно было выпустить в продакшен в любой момент. Непрерывное развёртывание (continuous deployment) идёт на шаг дальше: каждое изменение, прошедшее пайплайн, автоматически попадает в продакшен. Непрерывное развёртывание требует непрерывной поставки, и обе требуют CI.\u003C/p>\n\u003Cp>Общий термин CI/CD охватывает весь путь от коммита до релиза. Для автотестов разница - в последствии прохождения: в CI оно разрешает слияние, при непрерывной поставке - релиз, при непрерывном развёртывании - выполняет его.\u003C/p>\n\u003Ch2 id=\"термины\" class=\"wiki-heading\">Термины\u003Cbutton type=\"button\" class=\"wiki-heading__anchor\" data-wiki-anchor=\"термины\" aria-label=\"Скопировать ссылку на раздел\" data-copied-label=\"Ссылка на раздел скопирована\">\u003Csvg class=\"wiki-heading__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M117.18,188.74a12,12,0,0,1,0,17l-5.12,5.12A58.26,58.26,0,0,1,70.6,228h0A58.62,58.62,0,0,1,29.14,127.92L63.89,93.17a58.64,58.64,0,0,1,98.56,28.11,12,12,0,1,1-23.37,5.44,34.65,34.65,0,0,0-58.22-16.58L46.11,144.89A34.62,34.62,0,0,0,70.57,204h0a34.41,34.41,0,0,0,24.49-10.14l5.11-5.12A12,12,0,0,1,117.18,188.74ZM226.83,45.17a58.65,58.65,0,0,0-82.93,0l-5.11,5.11a12,12,0,0,0,17,17l5.12-5.12a34.63,34.63,0,1,1,49,49L175.1,145.86A34.39,34.39,0,0,1,150.61,156h0a34.63,34.63,0,0,1-33.69-26.72,12,12,0,0,0-23.38,5.44A58.64,58.64,0,0,0,150.56,180h.05a58.28,58.28,0,0,0,41.47-17.17l34.75-34.75a58.62,58.62,0,0,0,0-82.91Z\"/>\u003C/svg>\u003Csvg class=\"wiki-heading__icon wiki-heading__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003C/button>\u003C/h2>\n\u003Cdiv class=\"wiki-table wiki-table--cols-2\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Термин\u003C/th>\n\u003Cth>Значение\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>Непрерывная интеграция (continuous integration)\u003C/td>\n\u003Ctd>Практика, при которой изменения вливаются в общую основную ветку не реже раза в день, а каждую интеграцию проверяет автоматическая сборка с тестами\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Пайплайн (pipeline)\u003C/td>\n\u003Ctd>Последовательность заданий, которые CI-система выполняет для одного события: сборка, тесты, публикация, развёртывание\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Шлюз качества (quality gate)\u003C/td>\n\u003Ctd>Этап пайплайна, падение которого останавливает пайплайн\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Шардирование (sharding)\u003C/td>\n\u003Ctd>Разбиение набора тестов на части, которые выполняются как независимые задания на разных машинах\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Артефакт (artifact)\u003C/td>\n\u003Ctd>Файл, сохранённый заданием CI: отчёт, снимок экрана, трассировка, журнал\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Анализ влияния изменений на тесты (test impact analysis)\u003C/td>\n\u003Ctd>Техника, которая выбирает подмножество тестов для запуска при данном наборе изменений\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Непрерывная поставка (continuous delivery)\u003C/td>\n\u003Ctd>Практика, при которой продукт поддерживается в состоянии, пригодном для выпуска в продакшен в любой момент\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Непрерывное развёртывание (continuous deployment)\u003C/td>\n\u003Ctd>Практика, при которой каждое изменение, прошедшее пайплайн, автоматически выпускается в продакшен\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\u003C/div>\n\u003Ch2 id=\"источники\" class=\"wiki-heading\">Источники\u003Cbutton type=\"button\" class=\"wiki-heading__anchor\" data-wiki-anchor=\"источники\" aria-label=\"Скопировать ссылку на раздел\" data-copied-label=\"Ссылка на раздел скопирована\">\u003Csvg class=\"wiki-heading__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M117.18,188.74a12,12,0,0,1,0,17l-5.12,5.12A58.26,58.26,0,0,1,70.6,228h0A58.62,58.62,0,0,1,29.14,127.92L63.89,93.17a58.64,58.64,0,0,1,98.56,28.11,12,12,0,1,1-23.37,5.44,34.65,34.65,0,0,0-58.22-16.58L46.11,144.89A34.62,34.62,0,0,0,70.57,204h0a34.41,34.41,0,0,0,24.49-10.14l5.11-5.12A12,12,0,0,1,117.18,188.74ZM226.83,45.17a58.65,58.65,0,0,0-82.93,0l-5.11,5.11a12,12,0,0,0,17,17l5.12-5.12a34.63,34.63,0,1,1,49,49L175.1,145.86A34.39,34.39,0,0,1,150.61,156h0a34.63,34.63,0,0,1-33.69-26.72,12,12,0,0,0-23.38,5.44A58.64,58.64,0,0,0,150.56,180h.05a58.28,58.28,0,0,0,41.47-17.17l34.75-34.75a58.62,58.62,0,0,0,0-82.91Z\"/>\u003C/svg>\u003Csvg class=\"wiki-heading__icon wiki-heading__icon--done\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"/>\u003C/svg>\u003C/button>\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Ca href=\"https://martinfowler.com/articles/continuousIntegration.html\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">Continuous Integration, Martin Fowler, редакция 18 января 2024 года\u003Csvg class=\"wiki-link__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M228,104a12,12,0,0,1-24,0V69l-59.51,59.51a12,12,0,0,1-17-17L187,52H152a12,12,0,0,1,0-24h64a12,12,0,0,1,12,12Zm-44,24a12,12,0,0,0-12,12v64H52V84h64a12,12,0,0,0,0-24H48A20,20,0,0,0,28,80V208a20,20,0,0,0,20,20H176a20,20,0,0,0,20-20V140A12,12,0,0,0,184,128Z\"/>\u003C/svg>\u003Cspan class=\"wiki-visually-hidden\">(откроется в новой вкладке)\u003C/span>\u003C/a> (на английском)\u003C/li>\n\u003Cli>\u003Ca href=\"https://martinfowler.com/bliki/ContinuousDelivery.html\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">Continuous Delivery, Martin Fowler, 30 мая 2013 года\u003Csvg class=\"wiki-link__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M228,104a12,12,0,0,1-24,0V69l-59.51,59.51a12,12,0,0,1-17-17L187,52H152a12,12,0,0,1,0-24h64a12,12,0,0,1,12,12Zm-44,24a12,12,0,0,0-12,12v64H52V84h64a12,12,0,0,0,0-24H48A20,20,0,0,0,28,80V208a20,20,0,0,0,20,20H176a20,20,0,0,0,20-20V140A12,12,0,0,0,184,128Z\"/>\u003C/svg>\u003Cspan class=\"wiki-visually-hidden\">(откроется в новой вкладке)\u003C/span>\u003C/a> (на английском)\u003C/li>\n\u003Cli>\u003Ca href=\"https://martinfowler.com/articles/rise-test-impact-analysis.html\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">The Rise of Test Impact Analysis, Paul Hammant, 2017\u003Csvg class=\"wiki-link__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M228,104a12,12,0,0,1-24,0V69l-59.51,59.51a12,12,0,0,1-17-17L187,52H152a12,12,0,0,1,0-24h64a12,12,0,0,1,12,12Zm-44,24a12,12,0,0,0-12,12v64H52V84h64a12,12,0,0,0,0-24H48A20,20,0,0,0,28,80V208a20,20,0,0,0,20,20H176a20,20,0,0,0,20-20V140A12,12,0,0,0,184,128Z\"/>\u003C/svg>\u003Cspan class=\"wiki-visually-hidden\">(откроется в новой вкладке)\u003C/span>\u003C/a> (на английском)\u003C/li>\n\u003Cli>\u003Ca href=\"https://www.istqb.org/wp-content/uploads/2024/11/ISTQB_CTAL-TAE_Syllabus_v2.0.pdf\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">ISTQB Certified Tester Advanced Level Test Automation Engineering Syllabus v2.0, ISTQB, 2024\u003Csvg class=\"wiki-link__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M228,104a12,12,0,0,1-24,0V69l-59.51,59.51a12,12,0,0,1-17-17L187,52H152a12,12,0,0,1,0-24h64a12,12,0,0,1,12,12Zm-44,24a12,12,0,0,0-12,12v64H52V84h64a12,12,0,0,0,0-24H48A20,20,0,0,0,28,80V208a20,20,0,0,0,20,20H176a20,20,0,0,0,20-20V140A12,12,0,0,0,184,128Z\"/>\u003C/svg>\u003Cspan class=\"wiki-visually-hidden\">(откроется в новой вкладке)\u003C/span>\u003C/a> (на английском)\u003C/li>\n\u003Cli>\u003Ca href=\"https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">Events that trigger workflows, документация GitHub\u003Csvg class=\"wiki-link__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M228,104a12,12,0,0,1-24,0V69l-59.51,59.51a12,12,0,0,1-17-17L187,52H152a12,12,0,0,1,0-24h64a12,12,0,0,1,12,12Zm-44,24a12,12,0,0,0-12,12v64H52V84h64a12,12,0,0,0,0-24H48A20,20,0,0,0,28,80V208a20,20,0,0,0,20,20H176a20,20,0,0,0,20-20V140A12,12,0,0,0,184,128Z\"/>\u003C/svg>\u003Cspan class=\"wiki-visually-hidden\">(откроется в новой вкладке)\u003C/span>\u003C/a> (на английском)\u003C/li>\n\u003Cli>\u003Ca href=\"https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">Workflow syntax for GitHub Actions, документация GitHub\u003Csvg class=\"wiki-link__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M228,104a12,12,0,0,1-24,0V69l-59.51,59.51a12,12,0,0,1-17-17L187,52H152a12,12,0,0,1,0-24h64a12,12,0,0,1,12,12Zm-44,24a12,12,0,0,0-12,12v64H52V84h64a12,12,0,0,0,0-24H48A20,20,0,0,0,28,80V208a20,20,0,0,0,20,20H176a20,20,0,0,0,20-20V140A12,12,0,0,0,184,128Z\"/>\u003C/svg>\u003Cspan class=\"wiki-visually-hidden\">(откроется в новой вкладке)\u003C/span>\u003C/a> (на английском)\u003C/li>\n\u003Cli>\u003Ca href=\"https://docs.gitlab.com/ci/testing/unit_test_reports/\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">Unit test reports, документация GitLab\u003Csvg class=\"wiki-link__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M228,104a12,12,0,0,1-24,0V69l-59.51,59.51a12,12,0,0,1-17-17L187,52H152a12,12,0,0,1,0-24h64a12,12,0,0,1,12,12Zm-44,24a12,12,0,0,0-12,12v64H52V84h64a12,12,0,0,0,0-24H48A20,20,0,0,0,28,80V208a20,20,0,0,0,20,20H176a20,20,0,0,0,20-20V140A12,12,0,0,0,184,128Z\"/>\u003C/svg>\u003Cspan class=\"wiki-visually-hidden\">(откроется в новой вкладке)\u003C/span>\u003C/a> (на английском)\u003C/li>\n\u003Cli>\u003Ca href=\"https://playwright.dev/docs/test-sharding\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">Sharding, документация Playwright\u003Csvg class=\"wiki-link__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M228,104a12,12,0,0,1-24,0V69l-59.51,59.51a12,12,0,0,1-17-17L187,52H152a12,12,0,0,1,0-24h64a12,12,0,0,1,12,12Zm-44,24a12,12,0,0,0-12,12v64H52V84h64a12,12,0,0,0,0-24H48A20,20,0,0,0,28,80V208a20,20,0,0,0,20,20H176a20,20,0,0,0,20-20V140A12,12,0,0,0,184,128Z\"/>\u003C/svg>\u003Cspan class=\"wiki-visually-hidden\">(откроется в новой вкладке)\u003C/span>\u003C/a> (на английском)\u003C/li>\n\u003Cli>\u003Ca href=\"https://playwright.dev/docs/test-reporters\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">Reporters, документация Playwright\u003Csvg class=\"wiki-link__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M228,104a12,12,0,0,1-24,0V69l-59.51,59.51a12,12,0,0,1-17-17L187,52H152a12,12,0,0,1,0-24h64a12,12,0,0,1,12,12Zm-44,24a12,12,0,0,0-12,12v64H52V84h64a12,12,0,0,0,0-24H48A20,20,0,0,0,28,80V208a20,20,0,0,0,20,20H176a20,20,0,0,0,20-20V140A12,12,0,0,0,184,128Z\"/>\u003C/svg>\u003Cspan class=\"wiki-visually-hidden\">(откроется в новой вкладке)\u003C/span>\u003C/a> (на английском)\u003C/li>\n\u003Cli>\u003Ca href=\"https://playwright.dev/docs/ci\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">Continuous Integration, документация Playwright\u003Csvg class=\"wiki-link__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M228,104a12,12,0,0,1-24,0V69l-59.51,59.51a12,12,0,0,1-17-17L187,52H152a12,12,0,0,1,0-24h64a12,12,0,0,1,12,12Zm-44,24a12,12,0,0,0-12,12v64H52V84h64a12,12,0,0,0,0-24H48A20,20,0,0,0,28,80V208a20,20,0,0,0,20,20H176a20,20,0,0,0,20-20V140A12,12,0,0,0,184,128Z\"/>\u003C/svg>\u003Cspan class=\"wiki-visually-hidden\">(откроется в новой вкладке)\u003C/span>\u003C/a> (на английском)\u003C/li>\n\u003Cli>\u003Ca href=\"https://docs.pytest.org/en/stable/how-to/output.html\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">Managing pytest's output, документация pytest\u003Csvg class=\"wiki-link__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M228,104a12,12,0,0,1-24,0V69l-59.51,59.51a12,12,0,0,1-17-17L187,52H152a12,12,0,0,1,0-24h64a12,12,0,0,1,12,12Zm-44,24a12,12,0,0,0-12,12v64H52V84h64a12,12,0,0,0,0-24H48A20,20,0,0,0,28,80V208a20,20,0,0,0,20,20H176a20,20,0,0,0,20-20V140A12,12,0,0,0,184,128Z\"/>\u003C/svg>\u003Cspan class=\"wiki-visually-hidden\">(откроется в новой вкладке)\u003C/span>\u003C/a> (на английском)\u003C/li>\n\u003Cli>\u003Ca href=\"https://maven.apache.org/surefire/maven-surefire-plugin/examples/rerun-failing-tests.html\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">Rerun failing tests, Maven Surefire Plugin\u003Csvg class=\"wiki-link__icon\" viewBox=\"0 0 256 256\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\u003Cpath d=\"M228,104a12,12,0,0,1-24,0V69l-59.51,59.51a12,12,0,0,1-17-17L187,52H152a12,12,0,0,1,0-24h64a12,12,0,0,1,12,12Zm-44,24a12,12,0,0,0-12,12v64H52V84h64a12,12,0,0,0,0-24H48A20,20,0,0,0,28,80V208a20,20,0,0,0,20,20H176a20,20,0,0,0,20-20V140A12,12,0,0,0,184,128Z\"/>\u003C/svg>\u003Cspan class=\"wiki-visually-hidden\">(откроется в новой вкладке)\u003C/span>\u003C/a> (на английском)\u003C/li>\n\u003C/ul>\n",[10,14,17,20,23,26,29,32,35,38,41,44,47],{"id":11,"level":12,"text":13},"какую-задачу-решает-ci",2,"Какую задачу решает CI",{"id":15,"level":12,"text":16},"события-запуска-пайплайна","События запуска пайплайна",{"id":18,"level":12,"text":19},"этапы-по-скорости","Этапы по скорости",{"id":21,"level":12,"text":22},"параллельность-и-шардирование","Параллельность и шардирование",{"id":24,"level":12,"text":25},"отчёты-о-тестах","Отчёты о тестах",{"id":27,"level":12,"text":28},"повторы-упавших-тестов","Повторы упавших тестов",{"id":30,"level":12,"text":31},"артефакты","Артефакты",{"id":33,"level":12,"text":34},"результаты-в-тестовых-прогонах-системы-управления-тестированием","Результаты в тестовых прогонах системы управления тестированием",{"id":36,"level":12,"text":37},"выбор-тестов-и-анализ-влияния-изменений","Выбор тестов и анализ влияния изменений",{"id":39,"level":12,"text":40},"окружения-и-тестовые-данные-в-ci","Окружения и тестовые данные в CI",{"id":42,"level":12,"text":43},"ci-непрерывная-поставка-и-непрерывное-развёртывание","CI, непрерывная поставка и непрерывное развёртывание",{"id":45,"level":12,"text":46},"термины","Термины",{"id":48,"level":12,"text":49},"источники","Источники",2010,1793718893839]