[{"data":1,"prerenderedAt":82},["ShallowReactive",2],{"wiki-test-automation-flaky-tests-ru":3},{"meta":4,"html":8,"headings":9,"words":81},{"title":5,"description":6,"published":7,"updated":7,"sourceUpdated":7},"Нестабильные (flaky) тесты","Нестабильные тесты: проходят и падают на одном коде. Причины, данные исследований, поиск по истории прогонов, карантин, повторы и их риски, исправление.","2026-11-03","\u003Cp>\u003Cstrong>Нестабильный тест\u003C/strong> (flaky test) - автотест, который на одном и том же коде то проходит, то падает: продукт, тест и конфигурация не меняются, а результат меняется. Падение такого теста не показывает, есть ли в продукте \u003Cspan class=\"wiki-term\" tabindex=\"0\" data-wiki-term=\"defect\">дефект\u003C/span>, а прохождение не показывает, что его нет. Нестабильные тесты называют также недетерминированными (non-deterministic) или плавающими (intermittent).\u003C/p>\n\u003Cp>Термин пришёл из инженерной практики. В 2011 году Мартин Фаулер описал проблему в статье «Eradicating Non-Determinism in Tests». В 2016 году в Google Testing Blog вышла статья Джона Микко «Flaky Tests at Google and How We Mitigate Them», которая определяет нестабильный результат как тест, показывающий и успешный, и неуспешный результат на одном и том же коде. Первое крупное эмпирическое исследование причин, «An Empirical Analysis of Flaky Tests» Луо, Харири, Элусси и Маринова, представлено на конференции FSE в 2014 году. Программа ISTQB Advanced Level Test Automation Engineering (версия 2.0, 2024 год) не использует слово flaky: она говорит о \u003Cspan class=\"wiki-term\" tabindex=\"0\" data-wiki-term=\"test-case\">тест-кейсах\u003C/span>, которые не дают надёжного результата, например из-за состояния гонки, и о плавающих сбоях. Средства запуска тестов используют flaky как статус: Playwright и Maven Surefire помечают тест, который упал и затем прошёл при повторе, как flaky.\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>Автотест полезен, когда его падение означает проблему в продукте. Нестабильный тест разрывает эту связь. После нескольких ложных падений команда начинает перезапускать \u003Cspan class=\"wiki-term\" tabindex=\"0\" data-wiki-term=\"ci-pipeline\">пайплайн\u003C/span> вместо того, чтобы читать сообщение об ошибке. Фаулер отмечает следствие: научившись не обращать внимания на падения одного теста, разработчики перестают замечать и настоящие падения других.\u003C/p>\n\u003Cp>Масштаб проблемы в большой кодовой базе Google опубликовал в 2016 году:\u003C/p>\n\u003Cul>\n\u003Cli>около 1,5 процента всех запусков тестов давали нестабильный результат;\u003C/li>\n\u003Cli>почти 16 процентов тестов были в той или иной мере нестабильными;\u003C/li>\n\u003Cli>около 84 процентов наблюдаемых переходов от прохождения к падению при тестировании после отправки изменений (post-submit) приходились на нестабильный тест.\u003C/li>\n\u003C/ul>\n\u003Cp>В проекте из 1000 тестов при доле нестабильных результатов 1,5 процента около 15 тестов упадут в прогоне по причинам, не связанным с изменением. Каждое такое падение блокирует слияние, отнимает время на анализ и снижает доверие к результатам всего набора.\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>Тест нестабилен, когда его результат зависит от того, чем тест не управляет. Обычные источники таких зависимостей описаны ниже.\u003C/p>\n\u003Ch3 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/h3>\n\u003Cp>Тест выполняет действие и проверяет результат раньше, чем закончилась асинхронная операция: запрос к серверу, отрисовка страницы, сообщение в очереди. Фиксированная пауза в тесте скрывает проблему, пока операция быстрее паузы. На нагруженном агенте CI операция длится дольше, и тест падает. Программа ISTQB CTAL-TAE называет жёстко заданные ожидания причиной многих падений тестов.\u003C/p>\n\u003Ch3 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/h3>\n\u003Cp>Один тест оставляет состояние, на которое опирается другой: запись в базе данных, файл, значение в кеше, статическую переменную. Второй тест проходит, когда выполняется после первого, и падает, когда порядок меняется, например при распределении тестов между параллельными исполнителями (workers). Фаулер рекомендует изолировать тесты, чтобы выполнение одного теста не влияло на другие.\u003C/p>\n\u003Ch3 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/h3>\n\u003Cp>Тесты с общими учётными записями или записями в базе мешают друг другу. Два теста, которые параллельно переименовывают одного и того же клиента, видят изменения друг друга. Тест, который ожидает пустой список, падает, когда другой тест добавил элемент. Данные с ограниченным сроком - токен, пробный период, забронированный слот - делают тест зависимым от даты запуска.\u003C/p>\n\u003Ch3 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/h3>\n\u003Cp>Тест зависит от сети, стороннего API, почтового сервера, общего \u003Cspan class=\"wiki-term\" tabindex=\"0\" data-wiki-term=\"test-environment\">тестового окружения\u003C/span>. Таймаут или временная ошибка такого сервиса роняет тест без изменения продукта. Фаулер предлагает заменять нестабильные удалённые сервисы тестовыми двойниками (test doubles) - заменителями, которые ведут себя как настоящий сервис. Роль играют и различия агентов: число ядер, объём памяти, разрешение экрана, установленные шрифты.\u003C/p>\n\u003Ch3 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/h3>\n\u003Cp>У продукта или у самого теста несколько потоков или процессов, и результат зависит от порядка их работы. Состояние гонки проявляется только при некоторых чередованиях операций. Такие дефекты часто оказываются настоящими дефектами продукта, а не только теста.\u003C/p>\n\u003Ch3 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/h3>\n\u003Cp>Случайные входные данные, случайные идентификаторы и обход неупорядоченных коллекций дают разное выполнение при каждом запуске. Тесты, которые читают системные часы, падают в конце месяца, в полночь, при переходе на летнее время или на агенте в другом часовом поясе. Фаулер рекомендует всегда оборачивать системные часы, чтобы тест мог их подменить.\u003C/p>\n\u003Ch3 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/h3>\n\u003Cp>Тест не освобождает соединение, файловый дескриптор, порт, процесс браузера или память. Следующие тесты падают, когда ресурс заканчивается, и падение проявляется в тесте, который не имеет отношения к утечке. Фаулер описывает пул ресурсов размером один как способ заставить упасть первый тест после утечки и тем указать на причину.\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>Луо, Харири, Элусси и Маринов изучили коммиты в репозиториях Apache Software Foundation, в сообщениях которых упоминаются плавающие или нестабильные тесты. Они нашли 486 коммитов, которые, вероятно, исправляют разные нестабильные тесты, не меньше чем в 51 проекте, и подробно разобрали 201 из них. Для 161 коммита удалось определить причину. Распределение причин:\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>Доля от 161\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>Асинхронное ожидание\u003C/td>\n\u003Ctd>74\u003C/td>\n\u003Ctd>45%\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Параллельное выполнение\u003C/td>\n\u003Ctd>32\u003C/td>\n\u003Ctd>20%\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Зависимость от порядка тестов\u003C/td>\n\u003Ctd>19\u003C/td>\n\u003Ctd>12%\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Утечка ресурсов\u003C/td>\n\u003Ctd>11\u003C/td>\n\u003Ctd>7%\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Сеть\u003C/td>\n\u003Ctd>10\u003C/td>\n\u003Ctd>6%\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Время\u003C/td>\n\u003Ctd>5\u003C/td>\n\u003Ctd>3%\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Ввод и вывод\u003C/td>\n\u003Ctd>4\u003C/td>\n\u003Ctd>2%\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Случайность\u003C/td>\n\u003Ctd>4\u003C/td>\n\u003Ctd>2%\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Операции с плавающей точкой\u003C/td>\n\u003Ctd>3\u003C/td>\n\u003Ctd>2%\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Неупорядоченные коллекции\u003C/td>\n\u003Ctd>1\u003C/td>\n\u003Ctd>1%\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\u003C/div>\n\u003Cp>Исследование также показывает, как тесты исправляли. 42 из 74 тестов с асинхронным ожиданием исправлены \u003Cspan class=\"wiki-term\" tabindex=\"0\" data-wiki-term=\"explicit-wait\">явным ожиданием\u003C/span> условия; 20 «исправлены» добавлением или увеличением паузы, что лишь снизило вероятность падения. Все исправления тестов, зависящих от порядка, устранили нестабильность, чаще всего за счёт подготовки или очистки общего состояния. В 38 случаях из 161 (24 процента) исправление затронуло и тестируемый код, и в большинстве таких случаев устранило настоящий дефект. 126 из 161 теста были нестабильными с момента написания.\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\u003Ch3 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/h3>\n\u003Cp>Прямой способ показать нестабильность - запустить тест несколько раз на одном коммите без изменений. Если результаты различаются, тест нестабилен по определению. Playwright запускает каждый тест несколько раз с опцией \u003Ccode>--repeat-each\u003C/code>, а упавшее задание можно повторить с \u003Ccode>--last-failed\u003C/code>, которая запускает только упавшие тесты. Повторный запуск подтверждает нестабильность, но не находит причину.\u003C/p>\n\u003Ch3 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/h3>\n\u003Cp>Каждый прогон набора автотестов даёт результат по каждому тесту. Когда результаты хранятся как история, нестабильность видна как переключения между passed и failed без значимого изменения:\u003C/p>\n\u003Cdiv class=\"wiki-code wiki-code--plain\">\u003Cpre>\u003Ccode>Test &quot;checkout &gt; pay with saved card&quot;, branch main\n\ncommit   run    result\na41f2c   #812   passed\na41f2c   #813   failed    rerun of the same commit\nb7d903   #814   passed\nc1e5aa   #815   passed\nc1e5aa   #816   failed    rerun of the same commit\nd90b4e   #817   passed\u003C/code>\u003C/pre>\u003C/div>\n\u003Cp>Два падения на коммитах, на которых тест также проходил, - признак нестабильного теста, а не регрессии. Тест, который падает в каждом запуске начиная с одного коммита, указывает на изменение. Результаты прогонов и их история в \u003Cspan class=\"wiki-term\" tabindex=\"0\" data-wiki-term=\"test-management-system\">системе управления тестированием\u003C/span> описаны в статье \u003Ca href=\"/ru/wiki/test-management/test-run\" class=\"wiki-link wiki-link--article\">Тестовый прогон\u003C/a>. Из такой истории получают метрики: долю прогонов с переключением и число переключений по тесту за период.\u003C/p>\n\u003Ch3 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/h3>\n\u003Cp>Некоторые причины проявляются только в определённых условиях. Чтобы их выявить, команды запускают отдельные задания:\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>Программа CTAL-TAE рекомендует при анализе неожиданного результата выполнить несколько изолированных тестов и отмечает, что мониторинг системных ресурсов может подсказать причину плавающих сбоев.\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>Карантин - практика, при которой нестабильный тест выносят из наборов, результаты которых определяют слияние или релиз, а сам тест продолжает выполняться отдельно. Фаулер описывает это как хранение недетерминированных тестов в отдельном наборе, отдельно от здоровых тестов. Статья Google описывает инструмент, который отправляет в карантин тесты со слишком высокой нестабильностью и заводит на каждый задачу (bug); там же сказано, что так можно скрыть настоящие дефекты.\u003C/p>\n\u003Cp>Правила карантина обычно определяют:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>критерий входа\u003C/strong>: например, два падения и прохождения на одном коммите за неделю;\u003C/li>\n\u003Cli>\u003Cstrong>владельца\u003C/strong>: команду, которая отвечает за тест, и дефект или задачу на исправление;\u003C/li>\n\u003Cli>\u003Cstrong>ограничение\u003C/strong>: Фаулер предлагает ограничить число тестов в карантине (например, восемью) или срок пребывания теста в нём (например, неделей);\u003C/li>\n\u003Cli>\u003Cstrong>критерий выхода\u003C/strong>: исправление влито, и тест проходит серию повторных запусков;\u003C/li>\n\u003Cli>\u003Cstrong>видимость\u003C/strong>: тесты в карантине по-прежнему запускаются, а их результаты попадают в отчёт, чтобы настоящую регрессию в них заметили.\u003C/li>\n\u003C/ul>\n\u003Cp>Тест, который остаётся в карантине без владельца, больше ничего не проверяет. Его удаление тогда - явное решение, а не побочный эффект.\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>Повтор (retry) - автоматический перезапуск упавшего теста в том же задании. Средства запуска тестов поддерживают его напрямую. В Playwright опция \u003Ccode>retries\u003C/code> задаёт максимальное число повторов, а тест, который сначала упал и прошёл при повторе, получает статус flaky:\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  retries: process.env.CI ? 2 : 0,\n  use: {\n    trace: 'on-first-retry',\n  },\n});\u003C/code>\u003C/pre>\u003C/div>\n\u003Cp>Трассировка записывается при первом повторе, поэтому нестабильное падение оставляет данные для анализа. С версии 1.52 опция \u003Ccode>failOnFlakyTests\u003C/code> (в командной строке \u003Ccode>--fail-on-flaky-tests\u003C/code>) делает прогон неуспешным, если хотя бы один тест нестабилен. В pytest плагин \u003Ccode>pytest-rerunfailures\u003C/code> добавляет опции \u003Ccode>--reruns\u003C/code>, \u003Ccode>--reruns-delay\u003C/code> и \u003Ccode>--only-rerun\u003C/code>; последняя повторяет только падения, которые соответствуют шаблону:\u003C/p>\n\u003Cdiv class=\"wiki-code\" data-language=\"bash\">\u003Cdiv class=\"wiki-code__header\">\u003Cspan class=\"wiki-code__language\">Bash\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>pytest --reruns 2 --reruns-delay 1 --only-rerun TimeoutError\u003C/code>\u003C/pre>\u003C/div>\n\u003Cp>В Maven Surefire есть свойство \u003Ccode>surefire.rerunFailingTestsCount\u003C/code>; тест, который прошёл при повторе, учитывается в сводке как нестабильный (flake), а его неуспешные попытки записываются в XML-отчёт как элементы \u003Ccode>flakyFailure\u003C/code>.\u003C/p>\n\u003Cp>Повторы уменьшают число заблокированных слияний, но у них есть цена:\u003C/p>\n\u003Cul>\n\u003Cli>настоящий плавающий дефект, например состояние гонки в продукте, проходит при повторе и попадает в продакшен; по данным Луо и соавторов, четверть исправлений нестабильных тестов затронула тестируемый код;\u003C/li>\n\u003Cli>набор работает дольше, потому что каждое нестабильное падение выполняется несколько раз;\u003C/li>\n\u003Cli>обнаружение задерживается: Google описывает правило, по которому падение засчитывается только после трёх падений подряд, и 15-минутный тест превращается в 45 минут ожидания;\u003C/li>\n\u003Cli>без отчётности нестабильность незаметно накапливается.\u003C/li>\n\u003C/ul>\n\u003Cp>Поэтому повторы сочетают с отчётностью: статус flaky записывается в историю, подсчитывается и считается поводом для исправления. Плавающий сбой, который оказался дефектом продукта, оформляют со всем, что нужно для воспроизведения; см. статью \u003Ca href=\"/ru/wiki/test-management/defect-report\" class=\"wiki-link wiki-link--article\">Отчёт о дефекте\u003C/a>. Как повторы настраивают в пайплайне, описано в статье \u003Ca href=\"/ru/wiki/test-automation/automated-tests-in-ci\" class=\"wiki-link wiki-link--article\">Автотесты в CI\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\u003Ch3 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/h3>\n\u003Cp>Фиксированная пауза либо тратит время впустую, либо слишком коротка. Тест ниже после сохранения ждёт две секунды, а затем один раз считает строки:\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('order appears in the list after saving', async ({ page }) =&gt; {\n  await page.goto('https://example.com/orders/new');\n  await page.getByLabel('Customer').fill('Anna');\n  await page.getByRole('button', { name: 'Save' }).click();\n  await page.waitForTimeout(2000);\n  expect(await page.getByRole('row').count()).toBe(2);\n});\u003C/code>\u003C/pre>\u003C/div>\n\u003Cp>Документация Playwright помечает \u003Ccode>page.waitForTimeout\u003C/code> как нерекомендуемый метод и указывает, что тесты, которые ждут заданное время, нестабильны по своей природе. Исправленная версия использует проверку, которая повторяется, пока условие не выполнится или не истечёт таймаут:\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('order appears in the list after saving', async ({ page }) =&gt; {\n  await page.goto('https://example.com/orders/new');\n  await page.getByLabel('Customer').fill('Anna');\n  await page.getByRole('button', { name: 'Save' }).click();\n  await expect(page.getByRole('row')).toHaveCount(2);\n});\u003C/code>\u003C/pre>\u003C/div>\n\u003Cp>Playwright ждёт и перед действиями: перед нажатием он проверяет, что элемент видим, неподвижен, получает события и доступен. Программа CTAL-TAE ранжирует три вида ожиданий: жёстко заданное ожидание - худшее, опрос изменения состояния с таймаутом - лучше, подписка на событие тестируемой системы - самое надёжное. Для состояния за пределами страницы в Playwright есть \u003Ccode>expect.poll\u003C/code>:\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('payment is confirmed by the back end', async ({ request }) =&gt; {\n  await expect.poll(async () =&gt; {\n    const response = await request.get('https://example.com/api/orders/1042');\n    return (await response.json()).status;\n  }, { timeout: 10000 }).toBe('PAID');\n});\u003C/code>\u003C/pre>\u003C/div>\n\u003Ch3 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/h3>\n\u003Cp>Тест создаёт нужные ему данные и потом удаляет их. Уникальные значения не дают параллельным тестам столкнуться:\u003C/p>\n\u003Cdiv class=\"wiki-code\" data-language=\"python\">\u003Cdiv class=\"wiki-code__header\">\u003Cspan class=\"wiki-code__language\">python\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 uuid\n\nimport pytest\n\n\n@pytest.fixture\ndef customer(api):\n    created = api.post(&quot;/customers&quot;, json={&quot;name&quot;: f&quot;test-{uuid.uuid4()}&quot;}).json()\n    yield created\n    api.delete(f&quot;/customers/{created['id']}&quot;)\n\n\ndef test_rename_customer(api, customer):\n    api.patch(f&quot;/customers/{customer['id']}&quot;, json={&quot;name&quot;: &quot;Renamed&quot;})\n    assert api.get(f&quot;/customers/{customer['id']}&quot;).json()[&quot;name&quot;] == &quot;Renamed&quot;\u003C/code>\u003C/pre>\u003C/div>\n\u003Cp>Здесь \u003Ccode>api\u003C/code> - фикстура проекта с HTTP-клиентом. Тест не зависит от записей, оставленных другими тестами, и его собственная запись не влияет на них.\u003C/p>\n\u003Ch3 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/h3>\n\u003Cp>Время задаёт тест, а не агент. В Playwright \u003Ccode>page.clock.setFixedTime\u003C/code> устанавливает время, которое видит страница, а опция \u003Ccode>timezoneId\u003C/code> - часовой пояс контекста браузера:\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.use({ timezoneId: 'Europe/Berlin' });\n\ntest('shows the end of the billing period', async ({ page }) =&gt; {\n  await page.clock.setFixedTime(new Date('2026-01-31T23:30:00+01:00'));\n  await page.goto('https://example.com/billing');\n  await expect(page.getByText('Billing period ends today')).toBeVisible();\n});\u003C/code>\u003C/pre>\u003C/div>\n\u003Cp>Нестабильные внешние сервисы заменяют тестовыми двойниками. В Playwright \u003Ccode>page.route\u003C/code> перехватывает запросы страницы и возвращает подготовленный ответ, и тест перестаёт зависеть от доступности настоящего API.\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>Таблица показывает три ситуации, которые в отчёте выглядят похоже.\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>Обычно проходит\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>Задача на тест, карантин\u003C/td>\n\u003Ctd>Отчёт о дефекте\u003C/td>\n\u003Ctd>Задача на тест\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\u003C/div>\n\u003Cp>Нестабильный тест и плавающий дефект нельзя различить только по результату. Оба дают прохождения и падения на одном коде. Разницу находят анализом: журналы, трассировки, воспроизведение под нагрузкой. Повтор, который превращает падение в прохождение, одинаково убирает сигнал в обоих случаях.\u003C/p>\n\u003Cp>Сломанный тест падает детерминированно, например после изменения интерфейса, под которое тест не обновили. Он не нестабилен: его падение воспроизводится и указывает на конкретное изменение.\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>Нестабильный тест (flaky test)\u003C/td>\n\u003Ctd>Автотест, который на одном и том же коде то проходит, то падает без изменений продукта, теста и конфигурации\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Зависимость от порядка тестов (test order dependency)\u003C/td>\n\u003Ctd>Зависимость результата теста от других тестов, которые выполняются раньше и оставляют общее состояние\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Карантин (quarantine)\u003C/td>\n\u003Ctd>Вынос нестабильного теста из наборов, которые определяют слияние или релиз; тест продолжает выполняться отдельно до исправления\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Повтор (retry)\u003C/td>\n\u003Ctd>Автоматический перезапуск упавшего теста в том же задании; тест, прошедший при повторе, отмечается как нестабильный\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Плавающий дефект (intermittent defect)\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://testing.googleblog.com/2016/05/flaky-tests-at-google-and-how-we.html\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">Flaky Tests at Google and How We Mitigate Them, John Micco, Google Testing Blog, 27 May 2016\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/nonDeterminism.html\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">Eradicating Non-Determinism in Tests, Martin Fowler, 14 April 2011\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://huang.isis.vanderbilt.edu/cs8395/paper/flakytest.pdf\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">Luo, Hariri, Eloussi, Marinov. An Empirical Analysis of Flaky Tests, FSE 2014\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://playwright.dev/docs/test-retries\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">Retries, документация 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/actionability\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">Auto-waiting, документация 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-assertions\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">Assertions, документация 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/clock\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">Clock, документация 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/api/class-testconfig\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">TestConfig, документация 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://github.com/pytest-dev/pytest-rerunfailures\" target=\"_blank\" rel=\"noopener\" class=\"wiki-link wiki-link--external\">pytest-rerunfailures, pytest-dev\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,21,24,27,30,33,36,39,42,45,48,51,54,57,60,63,66,69,72,75,78],{"id":11,"level":12,"text":13},"какую-проблему-создают-нестабильные-тесты",2,"Какую проблему создают нестабильные тесты",{"id":15,"level":12,"text":16},"причины-нестабильности","Причины нестабильности",{"id":18,"level":19,"text":20},"асинхронные-ожидания-и-тайминги",3,"Асинхронные ожидания и тайминги",{"id":22,"level":19,"text":23},"зависимость-от-порядка-тестов-и-общее-состояние","Зависимость от порядка тестов и общее состояние",{"id":25,"level":19,"text":26},"тестовые-данные","Тестовые данные",{"id":28,"level":19,"text":29},"окружение-и-внешние-сервисы","Окружение и внешние сервисы",{"id":31,"level":19,"text":32},"параллельное-выполнение","Параллельное выполнение",{"id":34,"level":19,"text":35},"случайность-время-и-часовые-пояса","Случайность, время и часовые пояса",{"id":37,"level":19,"text":38},"утечки-ресурсов","Утечки ресурсов",{"id":40,"level":12,"text":41},"данные-исследований","Данные исследований",{"id":43,"level":12,"text":44},"обнаружение-нестабильных-тестов","Обнаружение нестабильных тестов",{"id":46,"level":19,"text":47},"повторные-запуски-на-одном-коммите","Повторные запуски на одном коммите",{"id":49,"level":19,"text":50},"история-прогонов","История прогонов",{"id":52,"level":19,"text":53},"отдельные-задания","Отдельные задания",{"id":55,"level":12,"text":56},"карантин","Карантин",{"id":58,"level":12,"text":59},"повторы-и-их-риски","Повторы и их риски",{"id":61,"level":12,"text":62},"исправление-нестабильных-тестов","Исправление нестабильных тестов",{"id":64,"level":19,"text":65},"ожидание-условия-вместо-фиксированной-паузы","Ожидание условия вместо фиксированной паузы",{"id":67,"level":19,"text":68},"собственные-данные-для-каждого-теста","Собственные данные для каждого теста",{"id":70,"level":19,"text":71},"управляемое-время-и-внешние-сервисы","Управляемое время и внешние сервисы",{"id":73,"level":12,"text":74},"нестабильный-тест-плавающий-дефект-и-сломанный-тест","Нестабильный тест, плавающий дефект и сломанный тест",{"id":76,"level":12,"text":77},"термины","Термины",{"id":79,"level":12,"text":80},"источники","Источники",2026,1793718893735]