15 типичных ошибок начинающих автоматизаторов (и как их избежать)
Начинающие автоматизаторы часто наступают на одни и те же грабли: от отсутствия параметризации до связанных автотестов. В этой статье — разбор ошибок и советы, как писать тесты так, чтобы они жили долго и стабильно.
Вступление
Автоматизация тестирования — это не только про код и фреймворки, но и про подходы, архитектуру и опыт. Многие начинающие автоматизаторы быстро погружаются в написание тестов, но при этом совершают ошибки, которые кажутся «мелочами» — пока не вырастают в большие проблемы: нестабильные тесты, сложность поддержки или путаницу в результатах.
В этой статье я собрал подборку самых частых ошибок, которые встречаются у начинающих инженеров по автоматизации. Цель этой статьи — не высмеять или «поймать на ошибках», а помочь разобраться: почему эти ошибки вредны и как их избежать.
Если вы только начинаете свой путь в автоматизации — возможно, узнаете себя и сможете избежать этих граблей. А если вы уже опытный инженер — есть шанс найти знакомые ситуации и, может быть, дополнить этот список своими наблюдениями.
Примеры в статье приведены на Python, но описанные подходы и принципы одинаково применимы и к другим языкам программирования.
1. Ошибка: один тест — одна проверка
Одна из первых «мантр», которые слышат начинающие автоматизаторы:
«Один тест — одна проверка»
Идея правильная: тест должен быть понятным и легко диагностируемым. Когда тест проверяет слишком много разных вещей, его падение превращается в квест: «а что именно пошло не так?»
Однако эту идею часто воспринимают слишком буквально:
Формально — да, по одной проверке на тест. Но по сути это одна и та же функциональность (например, валидация формы логина), искусственно разорванная на отдельные кусочки. В итоге:
- тестов становится больше, чем нужно;
- поддержка усложняется;
- запуск тестов становится дольше;
- отчёты захламляются, а анализ результатов замедляется.
На практике важно другое: один тест — один сценарий или одна фича. Если логика проверки связана и естественным образом выполняется последовательно, её можно объединить:
Если этот тест упал — значит, что-то в сценарии «форма логина» работает неправильно. И это нормально: отладить причину проще, чем поддерживать три отдельных теста, которые проверяют одно и то же «флоу».
Вывод:
- Делите тесты по сценариям, а не по отдельным строчкам assert’ов.
- Если проверки логически связаны — оставляйте их в одном тесте.
- Если проверки независимы (например, разные фичи) — выносите их в отдельные тесты.
2. Ошибка: отсутствие test id
Одна из самых частых ошибок у начинающих автоматизаторов — игнорирование test id и привязка к хрупким селекторам:
На первый взгляд — всё работает. Но как только дизайнер или фронтендер поменяет структуру или класс, тесты начнут падать. Это не баг продукта, а проблема теста — он оказался слишком хрупким.
Современный подход — использование специальных атрибутов для тестирования (data-testid, automation-id, и т.п.). Они не зависят от внешнего вида и верстки:
Почему это важно:
- тесты становятся стабильнее;
- изменения UI не ломают сценарии;
- тесты быстрее пишутся и проще поддерживаются.
Вывод: используйте test id, а не хрупкие XPath и CSS селекторы — это простой способ сделать ваши тесты более надёжными и «живучими» в долгосрочной перспективе.
3. Ошибка: Allure-шаги внутри теста
Новички часто начинают использовать Allure прямо внутри тестов:
На первый взгляд это выглядит удобно — отчёт получается красивым и понятным. Но на практике, когда тестов становится сотни или тысячи, дублирование шагов внутри каждого теста превращается в боль. Если завтра поменяется логика или название шага — менять придётся во всех тестах вручную.
Как лучше?
Вынесите шаги на уровень клиента и используйте их один раз:
Теперь тесты становятся чистыми и читаемыми:
Что мы выиграли:
- шаги отображаются в отчёте Allure автоматически;
- тесты не захламлены техническими деталями;
- поддержку и изменения делать проще: если меняется логика — правим в одном месте.
4. Ошибка: отсутствие API клиентов
Частая ошибка начинающих автоматизаторов — обращаться к API напрямую прямо из тестов:
На маленьком проекте это кажется удобным и быстрым: зачем писать ещё один класс, если и так работает? Но в перспективе это создаёт серьёзные проблемы:
- URL и эндпоинты размазаны по тестам;
- при изменении API нужно править десятки или сотни тестов;
- тесты становятся «грязными» и плохо читаются.
Как лучше?
Используйте API‑клиенты. Да, кода изначально будет чуть больше, но в будущем это экономия времени и нервов:
Теперь тест выглядит чище:
Преимущества:
- при изменении URL или параметров правим в одном месте;
- тесты читаются как бизнес‑сценарии, а не как набор технических вызовов;
- проще добавить логи, обработку ошибок и Allure‑шаги внутри клиента, не трогая сами тесты.
5. Ошибка: отсутствие PageObject
Одна из классических ошибок новичков в UI‑автоматизации — писать селекторы и проверки прямо внутри тестов:
На первый взгляд это удобно: всё на месте, тест сразу виден целиком. Но через пару месяцев проект разрастается, появляются десятки и сотни тестов — и в каждом из них повторяются одни и те же селекторы и проверки. Любое изменение верстки превращается в боль: приходится править десятки тестов вручную.
Как лучше — PageObject
Используйте PageObject — выделяйте логику взаимодействия со страницей в отдельные классы:
Тест становится чище и лучше читается:
Преимущества:
- изменения в верстке правятся в одном месте (в PageObject);
- тесты читаются как бизнес‑сценарии, а не как технический набор команд;
- легче добавлять дополнительную логику (логи, шаги, ожидания) без изменения тестов.
6. Ошибка: отсутствие параметризации
Эта ошибка встречается очень часто у начинающих автоматизаторов. Обычно выглядит она примерно так:
А иногда ещё хуже — тест пишется один, а входные данные гоняются в цикле:
На первый взгляд кажется, что так проще — но результат получается не очень:
- если что-то падает, вы не знаете, на каких именно данных;
- отчёт о тестировании становится нечитаемым (в отчёте это будет один тест);
- сложнее управлять входными данными.
Как правильно?
Используйте встроенные возможности параметризации тестов, например в pytest:
Так каждый набор данных становится отдельным тестом:
- упал конкретный вариант → сразу видно, где ошибка;
- отчёт красивый и читаемый;
- код лаконичный и поддерживаемый.
7. Ошибка: отсутствие фикстур
У начинающих автоматизаторов часто встречается такой код:
На первый взгляд всё вроде нормально: тесты выполняются, проверка проходит. Но есть одна проблема — повторение кода. Каждый тест создаёт LoginPage вручную, и таких мест может быть десятки или даже сотни.
Как правильно?
Вместо дублирования нужно использовать фикстуры. Например, в pytest:
Теперь объект LoginPage создаётся одним местом (в фикстуре), а сами тесты стали чище и короче. Если завтра нужно будет, например, добавить авторизацию или настроить браузер для всех тестов — вы делаете это один раз в фикстуре, а не правите сотни тестов.
Бонус: уровни фикстур
- scope="function" — фикстура создаётся перед каждым тестом (по умолчанию).
- scope="module" — создаётся один раз на модуль.
- scope="session" — один раз на всю сессию (например, если нужно поднять базу данных или сервис).
8. Ошибка: использование «магических чисел»
Часто у начинающих автоматизаторов в тестах можно встретить вот такой код:
Сработает? Да, сработает. Но проблема в том, что «400» здесь непонятно что значит. Через месяц вы откроете тест и будете вспоминать: «А почему именно 400? Что мы тут проверяли? Это не найденный пользователь или ошибка валидации?»
Как правильно?
Вместо «магических чисел» используйте именованные константы, например, из стандартной библиотеки http:
Теперь тест читается без лишних пояснений. По коду сразу понятно, что мы ожидаем ошибку в запросе, а не какой-то «мистический 400». А если в будущем поменяется код (например, сервис начнёт возвращать 422), вы сможете легко найти все такие проверки и обновить их.
Вывод
- Используйте именованные константы (HTTPStatus, свои enum’ы или словари с кодами).
- Избегайте «магических» значений в коде: это делает тесты понятнее и поддерживаемее.
9. Ошибка: отсутствие тестовых классов или сьютов
Часто встречается ситуация, когда все тесты пишутся «плоско», без объединения в классы или тестовые сьюты:
На маленьком проекте это может показаться нормальным, но когда тестов становится сотни или тысячи — поддержка превращается в боль. Найти нужный тест или сгруппировать логику становится сложно.
Как правильно?
Объединяйте тесты по смыслу в классы (сьюты). Например, если тесты относятся к одной функциональности, лучше оформить их так:
Почему это важно?
- Структура. Когда тестов сотни, классы помогают быстро ориентироваться.
- Общие фикстуры. Можно подключать фикстуры для всего класса:
- Маркировка и запуск. Легче запускать целые группы (pytest -k TestFeature), а не отдельные тесты.
- Отчёты. В Allure и аналогах структура тестов отображается более читаемо.
Вывод
- Объединяйте тесты в классы/сьюты.
- Старайтесь, чтобы каждый класс тестировал одну конкретную область.
- Это сделает проект понятнее и поддерживаемее.
10. Ошибка: использование assert вместо expect
Одна из частых ошибок — использовать обычные Python-ассерты для проверки UI-элементов:
На первый взгляд всё нормально — проверка ведь работает. Но у Playwright (и некоторых других UI-фреймворков) есть собственный механизм проверок, специально заточенный под работу с асинхронным UI и ожиданиями:
Почему это важно?
- Ожидания внутри проверки. expect() автоматически дождётся, пока элемент появится, исчезнет или изменится. В то время как assert element.is_visible() проверяет состояние «здесь и сейчас», что часто приводит к флаки-тестам.
- Более читаемые ошибки. Если проверка упала, expect() выведет понятное сообщение с контекстом: «Ожидали, что элемент будет видим, но он не появился в течение X секунд».
- Лучшие отчёты. Использование expect() даёт более структурированную информацию в отчётах (например, в Allure).
Вывод
- Для UI-тестов всегда используйте expect, а не «голый» assert.
- assert хорошо подходит для чистых Python-проверок (например, числовых значений), но не для элементов UI.
11. Ошибка: хардкод значений вместо настроек
Иногда в коде тестов можно встретить что-то вроде:
На первый взгляд, это работает. Но жёстко зашитые значения (http://localhost:8000) создают серьёзные проблемы:
- поменялся домен или порт → придётся править десятки тестов;
- одно окружение для разработчиков, другое для CI → тесты ломаются;
- сложнее масштабировать проект на разные стенды.
Как делать правильно?
Выносите такие значения в конфигурацию:
А в тестах используйте их:
Дополнительно
- В реальных проектах используют не просто класс, а файлы настроек (.env, config.yaml) и библиотеки вроде pydantic или dynaconf.
- Это позволяет менять окружение одной переменной, без переписывания тестов.
12. Ошибка: хранение чувствительных данных в коде
Иногда встречается примерно такой код:
На первый взгляд — удобно: всё в одном месте, тест запускается «из коробки». Но это плохая практика по нескольким причинам:
- пароль хранится прямо в коде → он попадёт в репозиторий;
- при смене пароля придётся править код;
- если проект открытый (Open Source) или используется внешний CI/CD, данные могут утечь.
Как делать правильно?
Храните данные в переменных окружения:
Подключайте их через настройки (например, Pydantic Settings):
Или храните секреты в безопасном месте (Vault, Doppler, 1Password CLI).
Резюме
Никогда не храните пароли, токены или ключи прямо в коде. Это не только риск утечек, но и лишняя боль при поддержке тестов.
13. Ошибка: асинхронность без надобности
Что обычно происходит? Начинающий автоматизатор видит современный Python и думает: «Асинхронность — это же круто, значит надо делать всё async!» В итоге появляются такие тесты:
В чём проблема?
- Асинхронность добавляет сложность: нужны event-loop, поддержка в фреймворке, особая отладка.
- Но выгоды нет: запрос всё равно выполняется последовательно, тесты не становятся быстрее, зато появляются проблемы с совместимостью библиотек.
- Новичок тратит время на борьбу с RuntimeError: Event loop is closed, Task was destroyed but it is pending! и другими сюрпризами.
Как делать правильно?
Используйте асинхронность только там, где она реально нужна:
- если вы тестируете нативно асинхронное API (например, websockets);
- если библиотека тестирования или клиент изначально асинхронные (например, httpx.AsyncClient или playwright.async_api).
Во всех остальных случаях — обычный синхронный код проще и надёжнее:
14. Ошибка: связанные автотесты
Как выглядит на практике? Часто можно встретить такие тесты:
Здесь test_delete_user зависит от того, что test_create_user отработал успешно. Если первый тест упадёт — второй даже не имеет смысла запускать.
Почему это плохо?
- Автотесты должны быть изолированными: каждый тест можно запустить в любом порядке, хоть один, хоть все сразу.
- Такой подход ломает параллельный запуск: тесты начинают «драться» за общие данные.
- Если нужно прогнать только один тест (например, test_delete_user) — придётся запускать и test_create_user.
Как делать правильно?
Подготавливать данные внутри каждого теста или через фикстуры:
Также можно:
- Использовать фабрики или сидинг. Можно заранее «посеять» пользователей (seed data) и работать с ними, не полагаясь на результат других тестов.
- Делать тесты атомарными. Каждый тест отвечает только за один сценарий и может быть выполнен независимо от остальных.
15. Ошибка: использование «сырых данных» вместо моделей
Начинающие автоматизаторы часто передают данные напрямую в виде словарей:
На старте это кажется простым и быстрым решением. Но со временем проект разрастается:
- поля меняются;
- появляются вложенные структуры;
- кто-то случайно опечатался в ключе — и тест падает не там, где ожидаешь.
Как лучше?
Использовать модели данных:
- Pydantic (рекомендуется для сложных структур и валидации);
- dataclasses (как минимум, для читаемости и явной структуры).
Преимущества:
- автопроверка типов и обязательных полей;
- меньше опечаток и «магических ключей»;
- код тестов становится читабельнее и надёжнее.
Вывод:
Не передавайте «сырые словари» в коде. Используйте хотя бы dataclass, а лучше Pydantic-модели — это уменьшит количество скрытых ошибок и упростит поддержку тестов.
Вывод
Ошибки, которые делают начинающие автоматизаторы, чаще всего связаны не с языком программирования или выбором фреймворка, а с подходами и архитектурой. Даже идеально написанный тест не принесёт пользы, если он хрупкий, связан с другими тестами или требует правок в десятках мест при любом изменении продукта.
Главная мысль проста:
- Старайтесь писать тесты так, чтобы их было легко поддерживать.
- Используйте подходы и практики, которые давно зарекомендовали себя: PageObject, API‑клиенты, фикстуры, параметризацию, именованные константы.
- Не бойтесь рефакторить свои тесты и учиться у более опытных коллег.
Автоматизация тестирования — это инженерная работа. Чем раньше вы начнёте мыслить как инженер, а не как «просто человек, который пишет тесты», тем быстрее вы вырастете и избежите этих ошибок.
Если хотите подробнее посмотреть примеры кода и готовые проекты, рекомендую эти статьи: