Раздел 11 — Качество в команде

Автотесты — инструмент команды, а не отдельный проект тестировщика

Прогресс курса Раздел 11 из 12

Что вы освоите в этом разделе

4 академических часа: 2 теории, 2 практики. Регресс «Задачника» 1.1 выполняется на практике: ваш набор с ключом --tasker-version 1.1. Тестовый процесс в целом (план, отчёт, регресс вручную) разобран в разделе 11 курса ручного тестирования.
01

Стратегия автоматизации

Стратегия отвечает на три вопроса: что проверяем автотестами, на каком уровне пирамиды и в каком порядке это делаем. Её пишут на страницу-две и пересматривают раз в несколько выпусков.

ПорядокЧто автоматизироватьПочему первым
1Smoke: вход, главная операция, главная страницаМинимум кода, а ответ «сборка вообще жива?» нужен на каждый коммит
2Правила и расчёты — модульными тестамиДёшево, быстро, много вариантов данных; дефекты расчётов — самые дорогие
3API: операции, негативные случаи, права доступаСтабильно, не зависит от вёрстки; права доступа — безопасность
4Регресс по найденным дефектам: тест на каждый исправленныйИсправленный однажды дефект любит возвращаться
5Главные сценарии в браузере — немногоПроверяют, что части собраны вместе; дороги в поддержке

Что сознательно оставляют вручную: удобство, внешний вид, исследовательские проходы, редкие сценарии. Это тоже записывают в стратегию — иначе каждый новый человек в команде будет предлагать «автоматизировать всё».

02

Регресс новой версии

Новая версия — повод прогнать весь набор. Пример — вторая версия shop.py. В её описании изменений одна строка: «Бесплатная доставка — от 2500 ₽ вместо 3000 ₽». На первой версии набор раздела 03 зелёный:

$ pytest -q
.........                                                                [100%]
9 passed in 0.13s

На второй:

$ pytest -q --tb=line
.F....F..                                                                [100%]
=================================== FAILURES ===================================
E   assert 0 == 300
     +  where 0 = <function delivery at 0x7efd342f4860>(2999)
     +    where <function delivery at 0x7efd342f4860> = shop.delivery
~/qa/tests/test_delivery.py:9: assert 0 == 300
E   ValueError: could not convert string to float: '1 250.50'
~/qa/shop.py:30: ValueError: could not convert string to float: '1 250.50'
=========================== short test summary info ============================
FAILED tests/test_delivery.py::test_spb_below_3000 - assert 0 == 300
FAILED tests/test_parse.py::test_parse_with_space_and_comma - ValueError: cou...
2 failed, 7 passed in 0.12s

Два падения — и это два разных случая:

ТестПричинаЧто делать
test_spb_below_3000: доставка 0 вместо 300 при сумме 2999Требование изменилось: с 2500 ₽ доставка бесплатна. Тест устарелОбновить тест по новому требованию: границы 2499 и 2500
test_parse_with_space_and_comma: строка «1 250,50» не разбираетсяВ описании изменений про суммы ничего нет. Сломали то, что работало, — регрессияБаг-репорт; тест не трогать

Правило разбора: каждое падение сверяют с описанием изменений и требованиями. Если изменение заявлено — обновляют тест (и это тоже проверка: тест должен упасть там, где ожидали). Если нет — это регрессия, даже если «новое поведение тоже логичное». Обновлять тест «чтобы позеленел», не выяснив причину, — худшее, что можно сделать с набором.

03

Definition of Done

Критерии готовности (Definition of Done, DoD) — список, который должен выполниться, чтобы задачу считали сделанной. Пункты про тесты — в каждом DoD команды, которая всерьёз занимается качеством:

DoD делает автотесты частью работы, а не «потом, если останется время». Если тесты пишутся отдельно и позже, они всегда отстают от кода.

04

Ревью автотестов

Тест — тоже код, и в нём бывают ошибки. Самая опасная — тест, который проходит всегда, даже на сломанной программе. Чек-лист ревью:

ВопросПлохой признак
Тест вообще что-то проверяет?Нет assert или expect; проверка assert r вместо кода и тела
Тест может упасть?Ожидание вычислено тем же кодом, что проверяется; ожидание переписано из ответа программы
Ожидание обосновано?Не ссылается ни на требование, ни на документацию
Тест независим?Меняет общие данные без уборки; зависит от порядка и даты
Тест стабилен?sleep, чтение текста без expect, CSS-цепочки
По имени понятно, что сломалось?test_1, test_tasks, один тест на десять проверок
Пропуски объяснены?skip и xfail без reason и номера баг-репорта

Простой способ проверить, что тест может упасть: временно сломать проверяемое (поменять >= на >, ожидаемое значение) и убедиться, что тест покраснел. Так поступают со всеми тестами на дефекты: тест, который не падал на дефектной версии, ничего не доказывает.

05

Метрики и их ловушки

МетрикаПолезна дляЛовушка
Число автотестовВидно, растёт ли наборСделать цель — получите сотни тестов без проверок
Покрытие кодаНайти непроверенный кодВыполненный код ≠ проверенный (раздел 10)
Доля зелёных прогоновЗдоровье конвейераДостигается удалением и skip «неудобных» тестов
Время прогонаУдобство: длинный прогон перестают запускатьУскорение ценой удаления браузерных проверок
Доля плавающих тестовДоверие к наборуСкрывается автоматическим перезапуском
Дефекты, найденные пользователямиИтог работы всей команды по качествуРастёт с числом пользователей — сравнивать долю, а не штуки

Закон Гудхарта: метрика, ставшая целью, перестаёт быть хорошей метрикой. Метрики смотрят вместе и обсуждают причины, а не ставят план «покрытие 90% к концу квартала».

06

Нагрузочное тестирование

Функциональные автотесты отвечают «правильно ли работает». Нагрузочные — «как работает, когда пользователей много»: время ответа, сколько запросов в секунду выдерживает, когда начинаются ошибки.

ВидВопрос
НагрузочноеУкладывается ли время ответа в требования при ожидаемом числе пользователей?
СтрессовоеПри какой нагрузке система ломается и как она восстанавливается?
На стабильность (длительное)Не растёт ли память и время ответа за часы работы?

Главные показатели: время ответа по процентилям (медиана и 95-й процентиль: «95% запросов быстрее N мс»), пропускная способность (запросов в секунду) и доля ошибок. Среднее время почти бесполезно: десять быстрых ответов и один в 10 секунд дают «нормальное» среднее.

Locust — инструмент нагрузочного тестирования на Python: поведение пользователя описывают классом, Locust запускает нужное число таких пользователей одновременно. Ставится отдельно: pip install locust==2.47.0.

"""Нагрузка на «Задачник»: каждый виртуальный пользователь входит и работает со списком задач."""
from locust import HttpUser, between, task


class TaskerUser(HttpUser):
    wait_time = between(0.5, 1.5)

    def on_start(self):
        r = self.client.post("/api/login", json={"login": "ivan", "password": "Ivan2026pass"})
        self.client.headers["Authorization"] = "Bearer " + r.json()["token"]

    @task(5)
    def list_tasks(self):
        self.client.get("/api/tasks")

    @task(1)
    def stats(self):
        self.client.get("/api/stats")

Запуск без веб-интерфейса: 20 пользователей, по 10 новых в секунду, 15 секунд, против «Задачника» на этом же компьютере:

$ locust -f locustfile.py --headless -u 20 -r 10 -t 15s --host http://127.0.0.1:8000 --only-summary 2>&1 | grep -v "^\[" | sed -n "/Type/,\$p" | head -7
Type     Name                                                                          # reqs      # fails |    Avg     Min     Max    Med |   req/s  failures/s
--------|----------------------------------------------------------------------------|-------|-------------|-------|-------|-------|-------|--------|-----------
POST     /api/login                                                                        20     0(0.00%) |     23      11      42     22 |    1.37        0.00
GET      /api/stats                                                                        43     0(0.00%) |      3       2       6      3 |    2.95        0.00
GET      /api/tasks                                                                       250     0(0.00%) |      4       2      21      3 |   17.14        0.00
--------|----------------------------------------------------------------------------|-------|-------------|-------|-------|-------|-------|--------|-----------
         Aggregated                                                                       313     0(0.00%) |      5       2      42      3 |   21.46        0.00

Столбцы: число запросов и ошибок, время ответа в миллисекундах (среднее, минимум, максимум, медиана), запросов в секунду. Без ключа --headless Locust открывает веб-интерфейс на http://localhost:8089 с графиками.

Результат нагрузочного теста на своём ноутбуке говорит о ноутбуке, а не о сервере: генератор нагрузки и приложение делят один процессор. Настоящие замеры делают на стенде, похожем на рабочий, с генератором на отдельной машине. И никогда — против чужих сайтов: нагрузка без разрешения владельца — это DoS-атака, уголовно наказуемая (ст. 272, 273 УК РФ).

Ключевые выводы раздела

Запомните главное

  • Стратегия: smoke → модульные → API → тесты на исправленные дефекты → немного UI; что остаётся ручным — тоже записано
  • Каждое падение после новой версии сверяют с описанием изменений: устаревший тест обновляют, регрессию оформляют
  • Автотесты — пункт Definition of Done, а не работа «на потом»
  • Ревью тестов ищет главное: может ли тест упасть и обосновано ли ожидание
  • Метрики смотрят вместе и не превращают в план
  • Нагрузку меряют процентилями, на стенде, и только свои системы
Практические задания — на учебном портале. Задания, критерии оценивания, сдача работ и оценки преподавателя — в курсе на portal.nevabit.ru. Учётную запись выдаёт преподаватель.
Раздел 10: Отчёты, покрытие, CI Все разделы Раздел 12: Итоговый проект