Skip to content

Мутационный гейт: Stryker считает выжившими мутантов, при прогоне которых не выполнилось ни одного теста #274

Description

@tihonove

В #272 мутационный гейт полдня держал PR красным на пустом месте: он объявлял выжившими мутантов, при прогоне которых не выполнялось ни одного теста.

Что происходит

В reports/mutation/mutation.json у таких мутантов testsCompleted: 0 при сотне покрывающих:

CallExpression  110:13   coveredBy: 99   testsCompleted: 0
LogicalOperator 122:17   coveredBy: 11   testsCompleted: 0

В trace-логе Stryker'а тот же прогон выглядит так:

Test Files  2 skipped (5)
     Tests  33 skipped (47)
   Duration  309ms ... tests 0ms

Ни одного упавшего теста нет — и Stryker пишет «мутант выжил».

Набор таких мутантов плавает от прогона к прогону. Один и тот же коммит на CI: 5 выживших → перезапуск той же джобы → 6 других. Локально по одному файлу два прогона подряд дали разных единственных выживших. При этом каждый из них при ручной подстановке в код валит от одного до четырёх тестов — то есть тесты их убивают, гейт просто их не запускает.

Что не помогает:

режим результат
coverageAnalysis: perTest (как сейчас) выжившие с testsCompleted: 0
coverageAnalysis: off то же, другой набор
--maxTestRunnerReuse 1 хуже (5 вместо 2)
vitest.related: false dry-run падает на постороннем тесте diffing fixtures difficult-move

Похоже на дефект связки @stryker-mutator/vitest-runner@10vitest@4: в логе рядом висит DEPRECATED test.poolOptions was removed in Vitest 4 — раннер отдаёт vitest конфиг старого формата. Тем же классом хрупкости выглядит и RuntimeError: Test runner crashed. Tried twice to restart it, который иногда прилетает на отдельных мутантах (порог он не ломает — Stryker считает ошибки отдельной колонкой).

Что уже сделано

В #272 гейт (scripts/mutation-diff.mjs) научился различать «никто не заметил» и «ничего не запускали»: выживший с testsCompleted > 0 роняет прогон сразу, а нулевые перепроверяются вторым точечным прогоном по диапазону строк самого мутанта — там прогон надёжен. Перепроверенные помечаются в отчёте Killed с причиной; пустая перепроверка — падение, а не зелёный.

Это обходной путь, а не лечение.

Что стоит сделать дальше

  1. Собрать минимальный воспроизводимый пример и завести issue в апстриме StrykerJS (или найти существующий — судя по симптомам, это должно всплывать у всех, кто на vitest 4).
  2. Проверить, чинит ли проблему новая версия раннера, когда выйдет; после этого обходной путь из mutation-diff.mjs снять, а врезку из docs/TESTING.md («Выжил с нулём выполненных тестов — не находка») убрать вместе с ним.
  3. Заодно посмотреть на RuntimeError: Test runner crashed — сейчас он не влияет на порог, но это тот же симптом.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions