Регрессионное тестирование: когда запускать, как выбрать тесты и автоматизировать

Регрессионное тестирование: когда запускать, как выбрать тесты и автоматизировать Полезное

Регрессионное тестирование — это повторная проверка уже работавшей функциональности после изменения в коде, конфигурации или окружении, чтобы убедиться, что она не сломалась. То, что работало и перестало, называют регрессией (регрессионным дефектом).

Регресс легко спутать с двумя соседними проверками. Ретест (в терминах ISTQB — confirmation testing) отвечает на вопрос «исправлен ли конкретный баг». Смоук-тест — «пригодна ли сборка для дальнейшей проверки вообще». Регресс спрашивает третье: «не сломалось ли что-то рядом и дальше». Ниже — когда его запускать, как собрать набор тестов и как автоматизировать его в CI на примере pytest.

Смоук, ретест и регресс: в чем разница

Проверка Вопрос Объем Когда
Смоук (smoke) Сборка вообще работает? 5-30 ключевых сценариев, минуты Сразу после сборки или деплоя на стенд
Ретест (confirmation) Исправлен ли именно этот баг? Шаги из баг-репорта После фикса дефекта
Регресс (regression) Не сломалось ли то, что работало? От части набора до полного После любого изменения, перед релизом
Санити (sanity) Разумно ли ведет себя измененная область? Узко, вокруг изменения После небольшой правки

Термин «санити» в разных источниках трактуют по-разному, иногда как синоним смоука, — важнее договориться о словах внутри команды.

Смоук отсеивает сломанную сборку, ретест подтверждает фикс, регресс проверяет последствия. Успешный ретест не означает, что регресс можно пропустить: фикс мог задеть соседний модуль.

Когда запускать регрессионное тестирование

Поводом служит любое изменение, которое может повлиять на уже работающее поведение:

  • исправление дефекта (фикс в одном месте ломает другое чаще, чем кажется);
  • новая функциональность, которая использует общий код или данные;
  • рефакторинг, даже если «поведение не менялось»;
  • обновление зависимостей: библиотеки, фреймворка, драйвера БД, версии языка;
  • смена окружения: ОС, версии СУБД, браузера, конфигурации сервера;
  • слияние веток и подготовка релиза.

На практике регресс запускают слоями разной глубины: быстрый срез — часто, полный — реже.

Событие Что запускать Типичное время
Коммит или merge request Смоук + тесты измененного модуля Минуты
Слияние в основную ветку Полный автоматический регресс Десятки минут
Ночная сборка Длинные e2e и интеграционные сценарии Часы
Перед релизом Полный автоматический регресс + ручные проверки рисковых зон По плану релиза

Время в таблице — ориентир для небольшого проекта; у крупной системы полный регресс может идти сутками.

Как выбрать набор тестов для регресса

Полный регресс (все тесты) надежнее, но дорог; частичный быстрее, но пропускает то, что не попало в выборку. Выбор делают по риску:

  1. Определить, что изменилось. Файлы и модули из diff, затронутые таблицы БД, API, конфигурация.
  2. Найти зависимости. Кто вызывает измененный код, кто читает те же данные, какие интеграции проходят через модуль.
  3. Добавить критичные бизнес-сценарии. Регистрация, оплата, оформление заказа — то, без чего продукт теряет деньги или пользователей. Они входят в любой срез.
  4. Добавить зоны с историей дефектов. Модули, где регрессии уже случались, ломаются снова.
  5. Добавить тесты на недавно исправленные баги. Каждый исправленный дефект стоит закрепить автотестом, чтобы он не вернулся незаметно.
  6. Расставить приоритеты. Высокий — критичные сценарии и измененная область; средний — зависимые модули; низкий — редко используемые функции. При нехватке времени срезают снизу.

Набор нужно поддерживать: удалять тесты на удаленную функциональность, чинить нестабильные. Регресс, который краснеет без причины, перестают читать.

Пример: регрессионный тест на pytest

Функция расчета суммы заказа, в которой уже исправляли баг #142: промокод в нижнем регистре не давал скидку. Проверено на Python 3.9 и pytest 8.4.

# pricing.py
def total_price(price: float, qty: int, promo: str = "") -> float:
    """Сумма заказа: скидка 10% по промокоду SALE10."""
    if qty < 0:
        raise ValueError("qty must be >= 0")
    total = price * qty
    if promo.strip().upper() == "SALE10":
        total *= 0.9
    return round(total, 2)

Тесты помечаем маркерами: smoke — быстрые базовые проверки, regression — закрепление исправленных дефектов.

# test_pricing.py
import pytest
from pricing import total_price


@pytest.mark.smoke
def test_basic_total():
    assert total_price(100, 3) == 300


@pytest.mark.smoke
def test_promo_applies():
    assert total_price(100, 1, "SALE10") == 90


@pytest.mark.regression
def test_promo_is_case_insensitive():
    # баг #142: промокод в нижнем регистре не давал скидку
    assert total_price(100, 1, "sale10") == 90


@pytest.mark.regression
def test_negative_qty_rejected():
    with pytest.raises(ValueError):
        total_price(100, -1)

Маркеры регистрируются в pytest.ini рядом с тестами. Опция --strict-markers превращает опечатку в имени маркера в ошибку — зачем это нужно, видно ниже.

[pytest]
markers =
    smoke: быстрые проверки, что сборка работоспособна
    regression: тесты на ранее исправленные дефекты
addopts = --strict-markers

Запуск по слоям. -m smoke выбирает только смоук, без фильтра идет весь набор:

$ pytest -m smoke -q
..                                                     [100%]
2 passed, 2 deselected in 0.03s

$ pytest -q
....                                                   [100%]
4 passed in 0.02s

Как регресс ловит возврат бага

Допустим, кто-то «упростил» условие до if promo == "SALE10":. Смоук остается зеленым — в нем промокод передается заглавными буквами. Полный прогон показывает регрессию:

$ pytest -q
..F.                                                   [100%]
>       assert total_price(100, 1, "sale10") == 90
E       AssertionError: assert 100 == 90
FAILED test_pricing.py::test_promo_is_case_insensitive
1 failed, 3 passed in 0.04s

Смоук подтвердил, что сборка жива, но без регресса старый дефект ушел бы в релиз.

Ошибка: опечатка в маркере

Неверно: в тестах написано @pytest.mark.regresion (одна буква «s»), а --strict-markers в конфиге нет.

Результат: pytest выдает только предупреждение PytestUnknownMarkWarning, а команда pytest -m regression не выбирает ни одного теста (4 deselected). Если в CI отбор идет выражением вроде -m "smoke or regression", пайплайн будет зеленым, хотя регресс фактически не выполнялся.

Исправление: зарегистрировать маркеры и включить --strict-markers, как в конфиге выше. Тогда опечатка останавливает сбор тестов:

ERROR test_pricing.py - Failed: 'regresion' not found in `markers` configuration option
1 error in 0.05s

Автоматизация регресса в CI

Регресс — первый кандидат на автоматизацию: одни и те же проверки повторяются на каждое изменение. Типовая схема в CI (GitLab CI, GitHub Actions, Jenkins устроены похоже):

  • на merge request — pytest -m smoke и тесты затронутого модуля; падение блокирует слияние;
  • после слияния в основную ветку — полный pytest с отчетом --junitxml=report.xml, который CI показывает в интерфейсе;
  • по расписанию — длинные e2e-сценарии (UI через Playwright или Selenium, API-цепочки);
  • после падения — pytest --lf перезапускает только упавшие в прошлый раз тесты, чтобы быстро проверить фикс.

Большой набор ускоряет параллельный запуск, например плагином pytest-xdist (pytest -n auto), если тесты не зависят друг от друга и от общего состояния.

Автоматизировать Оставить ручным
Стабильные сценарии, которые повторяются каждый релиз Новую функциональность, пока требования меняются
Расчеты, API, бизнес-правила Удобство интерфейса и визуальное восприятие
Тесты на исправленные баги Исследовательское тестирование рисковых зон
Проверки на разных данных и окружениях Разовые проверки, которые дешевле пройти руками

Если регресс не работает: симптомы и что делать

  • Прогон идет часами, его перестали запускать. Разделить набор на слои (смоук / модуль / полный), распараллелить, вынести медленные e2e в ночную сборку.
  • Тесты падают случайно (flaky). Не перезапускать до зеленого: изолировать тест в отдельную группу, найти причину — общее состояние, время, порядок запуска, сетевые зависимости.
  • Регресс зеленый, а баг дошел до пользователей. Написать тест на дефект и найти, почему область не попала в выборку (часто — неучтенная зависимость, шаг 2).

Выводы

  • Регрессионное тестирование проверяет, что изменение не сломало уже работавшее поведение; ретест проверяет сам фикс, смоук — работоспособность сборки.
  • Повод для регресса — любое изменение: фикс, новая функция, рефакторинг, обновление зависимостей или окружения.
  • Набор выбирают по риску: изменения и их зависимости, критичные сценарии, зоны с историей дефектов, тесты на исправленные баги.
  • Регресс удобно запускать слоями: быстрый срез на каждый merge request, полный прогон после слияния и перед релизом.
  • В pytest слои задаются маркерами; --strict-markers защищает от опечатки, из-за которой регресс молча не выполняется.

Где применяется / связь с практикой

Освойте тему на практике

Регрессионный набор — основная рабочая зона инженера по автоматизации тестирования: какие проверки автоматизировать, как разложить их по слоям и встроить в CI. Если хочется освоить это на практике — автотесты на Python, pytest, UI- и API-проверки, — посмотри программу курса «Автоматизация тестирования веб-приложений на Python».

Освойте тему на практике

Разобраться в отдельных темах тестирования и задать вопросы практикующим инженерам можно на бесплатных открытых уроках Otus.

FAQ

Можно ли проводить регрессионное тестирование полностью вручную?
Можно, на небольших проектах так часто делают по чек-листу. С ростом продукта ручной регресс дорожает, и стабильные сценарии переводят в автотесты.

Кто пишет регрессионные тесты — тестировщик или разработчик?
Зависит от команды: модульные тесты на исправленный баг часто пишет разработчик вместе с фиксом, а сквозные UI- и API-сценарии — инженер по автоматизации тестирования. Важно, чтобы тест на дефект появлялся в том же изменении, что и исправление.

Нужен ли регресс, если покрытие кода тестами 100%?
Нужен: покрытие показывает, какие строки выполнялись при тестах, но не гарантирует, что проверены все значимые комбинации данных и сценарии.

OTUS Журнал
Скидка 5% 12-19 октября на курсы (popup)
Бесплатные открытые уроки (поп-ап)