Автоматизация и инженерия тестов
Практика автоматизации — unit- и e2e-тесты, паттерн Page Object, борьба с flaky-тестами, локаторы, тестовые данные, CI/CD и инженерная интуиция.
23 вопросов
JuniorТеорияОчень частоЧто такое end-to-end (e2e) тест?
Что такое end-to-end (e2e) тест?
E2e-тест прогоняет всю систему так, как это делал бы реальный пользователь — через UI и все слои (фронт, бэкенд, база), чтобы проверить, что цельный пользовательский поток работает. Он даёт наивысшую уверенность в работе продукта в целом, но это самый медленный, хрупкий и дорогой вид теста, поэтому в пирамиде их держат мало.
Типичные ошибки
- ✗Путать e2e с unit-тестом
- ✗Думать, что e2e-тесты быстрые и дешёвые в поддержке
- ✗Считать, что пирамида хочет больше всего e2e-тестов
Уточняющие вопросы
- →Почему пирамида тестирования держит e2e-тестов мало?
- →Почему e2e-тесты более склонны к flakiness?
MiddleТеорияОчень частоЧто такое assert, какие бывают виды и какие инструменты их дают?
Что такое assert, какие бывают виды и какие инструменты их дают?
Assert — это проверка внутри теста, которая роняет его, когда ожидаемое условие не выполняется. Виды: assertTrue/assertFalse, assertEquals/assertNotEquals, assertNull, assertThrows, плюс различие soft и hard (soft-asserts собирают сбои и продолжают, hard-asserts останавливаются сразу). Инструменты: JUnit/TestNG, AssertJ, Hamcrest, pytest, Chai.
Типичные ошибки
- ✗Думать, что assert не может уронить тест
- ✗Путать soft-asserts (продолжают) с hard-asserts (останавливают)
- ✗Считать, что фреймворки не дают методов assert
Уточняющие вопросы
- →Когда soft-assert предпочтительнее hard-assert?
- →Почему один логический assert на тест — полезное правило?
MiddleТеорияОчень частоЧто такое flaky-тесты и что с ними делать?
Что такое flaky-тесты и что с ними делать?
Flaky-тест то проходит, то падает недетерминированно на одном и том же коде. Частые причины: гонки по времени, неявные ожидания, зависимость от порядка тестов, общее или грязное состояние и нестабильность сети. Чинят заменой sleep на явные ожидания, изоляцией состояния, снятием связи по порядку — затем тест карантинят и либо стабилизируют, либо удаляют, ведь flaky-тест подрывает доверие ко всему набору.
Типичные ошибки
- ✗Добавлять sleep вместо явных ожиданий
- ✗Оставлять flaky-тест в наборе вместо карантина
- ✗Винить код, когда причина — общее состояние или порядок
Уточняющие вопросы
- →Почему flaky-тест иногда хуже, чем его отсутствие?
- →Как зависимость от порядка тестов вызывает flakiness?
MiddleТеорияОчень частоЧто такое паттерн Page Object и где он применяется?
Что такое паттерн Page Object и где он применяется?
Page Object Model — это паттерн для UI-автоматизации: каждая страница или экран — это класс, выставляющий свои элементы и действия как методы, так что тесты вызывают методы страницы, а не сырые локаторы. Он сосредотачивает поддержку локаторов в одном месте, убирает дублирование и держит тесты читаемыми. Применяется в наборах Selenium, Appium и Playwright.
Типичные ошибки
- ✗Класть сырые локаторы в тесты вместо классов страниц
- ✗Думать, что он для unit-тестов, а не для UI-автоматизации
- ✗Считать, что он добавляет дублирование, а не убирает его
Уточняющие вопросы
- →Как паттерн снижает поддержку локаторов при изменении UI?
- →Что относится к page object, а что — к самому тесту?
MiddleТеорияЧастоКак сравнить кросс-платформенный Appium и нативные инструменты автоматизации?
Как сравнить кросс-платформенный Appium и нативные инструменты автоматизации?
Appium даёт одну кодовую базу, управляющую и iOS, и Android, так что тесты пишутся один раз — но он медленнее и более flaky и может отставать в поддержке новых функций ОС. Нативные инструменты (Espresso для Android, XCUITest для iOS) быстрее и стабильнее и живут в коде приложения, так что их могут писать разработчики, но требуют двух кодовых баз и примерно вдвое больше поддержки.
Типичные ошибки
- ✗Путать, какой вариант кросс-платформенный, а какой нативный
- ✗Думать, что нативные инструменты покрывают обе платформы из одной базы
- ✗Считать Appium самым быстрым и стабильным
Уточняющие вопросы
- →Когда двойная поддержка нативных инструментов всё же оправдана?
- →Почему Appium склонен отставать в поддержке новых функций ОС?
MiddleТеорияЧастоЧто такое CI/CD и какую проблему она решает?
Что такое CI/CD и какую проблему она решает?
CI (Continuous Integration) автоматически собирает и тестирует каждое изменение по мере его попадания, рано ловя проблемы интеграции. CD (Continuous Delivery/Deployment) автоматизирует доставку проверенных сборок к проду или в него. Вместе они решают integration hell и ошибки ручных релизов, давая быструю обратную связь и повторяемый, проверяемый путь от коммита до работающего ПО.
Типичные ошибки
- ✗Говорить, что CI деплоит без сборки и тестов
- ✗Думать, что CI/CD ручная, а не автоматизированная
- ✗Путать шаг интеграции с шагом доставки
Уточняющие вопросы
- →Где автотесты вписываются в CI-пайплайн?
- →Как CI ловит проблемы интеграции раньше, чем большой merge?
MiddleТеорияЧастоКаким должен быть хороший автотест с технической точки зрения?
Каким должен быть хороший автотест с технической точки зрения?
Хороший автотест независим и атомарен, детерминирован (не flaky) и читаем. У него понятные setup/teardown (фикстуры, предусловия, постусловия), он даёт понятное сообщение об ошибке и в идеале проверяет одну логическую вещь. Это ложится на принципы FIRST — Fast, Independent, Repeatable, Self-validating, Timely — делая набор надёжным и дешёвым в поддержке.
Типичные ошибки
- ✗Позволять тестам зависеть от порядка или состояния друг друга
- ✗Терпеть flakiness вместо стабилизации теста
- ✗Писать расплывчатые сообщения об ошибке или без ассерта
Уточняющие вопросы
- →Почему независимость теста важнее его количества?
- →Как понятное сообщение об ошибке ускоряет отладку?
MiddleТеорияЧастоКакие виды локаторов бывают и чем они различаются?
Какие виды локаторов бывают и чем они различаются?
Web-локаторы: id, name, class, tag, CSS-селектор, XPath и текст ссылки. id самый быстрый и стабильный; XPath самый гибкий, но хрупкий и медленный, поэтому CSS обычно предпочтительнее. На мобайле добавляются accessibility id и resource-id (Android). Компромисс всегда — стабильность против гибкости: выбирайте самый стабильный селектор, который позволяет разметка.
Типичные ошибки
- ✗Хвататься за XPath, когда хватит стабильного id или CSS
- ✗Думать, что все локаторы одинаково стабильны
- ✗Забывать мобильные локаторы (accessibility id, resource-id)
Уточняющие вопросы
- →Почему XPath-локатор более хрупкий, чем стабильный id?
- →Что делает локатор устойчивым к рефакторингу UI?
MiddleТеорияЧастоЧто такое мокирование и зачем оно в автотестах?
Что такое мокирование и зачем оно в автотестах?
Мокирование заменяет реальную зависимость — сервис, базу, часы или сетевой вызов — управляемой подменой, возвращающей заранее заданные ответы. Оно держит тест изолированным, быстрым и детерминированным, позволяет имитировать трудновоспроизводимые условия (ошибки, таймауты, закрытый ночью магазин) и убирает зависимость от внешнего состояния, которое вы не контролируете во время прогона.
Типичные ошибки
- ✗Думать, что мокирование использует реальную зависимость
- ✗Забывать, что моки позволяют имитировать ошибки и таймауты
- ✗Путать мокирование с копированием тестов
Уточняющие вопросы
- →Как замокать часы, чтобы протестировать зависящую от времени функцию?
- →Когда избыток моков делает тест менее ценным?
JuniorТеорияИногдаЧто такое линтер и что он делает?
Что такое линтер и что он делает?
Линтер — это статический анализатор, который читает исходный код без запуска и помечает нарушения стиля и вероятные ошибки — неиспользуемые переменные, неопределённые имена, подозрительные паттерны, расхождения форматирования. Он держит единый стиль кода и ловит класс багов рано, до того как код вообще выполнится, часто как часть CI-пайплайна.
Типичные ошибки
- ✗Думать, что линтер запускает код, а не читает его статически
- ✗Сводить его только к форматированию, игнорируя поиск ошибок
- ✗Путать его с рантайм-профайлером
Уточняющие вопросы
- →Чем запуск линтера в CI помогает команде?
- →Какие баги линтер не может поймать?
JuniorТеорияИногдаЧто такое нагрузочное тестирование и для чего оно?
Что такое нагрузочное тестирование и для чего оно?
Нагрузочное тестирование измеряет, как система ведёт себя под ожидаемой и пиковой нагрузкой — проверяет пропускную способность, время отклика и ёмкость и находит узкие места до того, как на них наткнутся пользователи. Инструменты: JMeter, k6, Gatling, Locust. Родственные подвиды — стресс (за пиком), спайк, soak/выносливость (длительный) и тестирование масштабируемости.
Типичные ошибки
- ✗Путать нагрузочное с функциональным или UI-тестированием
- ✗Считать нагрузочное и стресс-тестирование одним и тем же
- ✗Забывать, что оно нацелено на пропускную способность, отклик и ёмкость
Уточняющие вопросы
- →Чем нагрузочное тестирование отличается от стресс-тестирования?
- →Какие метрики говорят, что система достигла предела ёмкости?
JuniorТеорияИногдаЧто такое unit-тест и чем он ценен?
Что такое unit-тест и чем он ценен?
Unit-тест проверяет наименьшую изолированную часть кода — отдельную функцию или модуль — независимо от остальной системы, обычно с замоканными зависимостями. Он ценен тем, что быстро выполняется, точно указывает место дефекта, минимизирует регрессию, ловя поломки рано, и даёт разработчикам быструю обратную связь при изменении кода.
Типичные ошибки
- ✗Путать unit-тест с интеграционным или e2e-тестом
- ✗Думать, что unit-тестам нужны реальная база или сеть
- ✗Считать, что их нельзя автоматизировать
Уточняющие вопросы
- →Как мокирование сохраняет изоляцию unit-теста?
- →Где unit-тесты находятся в пирамиде тестирования?
MiddleТеорияИногдаЧто такое антипаттерн, с примерами из автоматизации тестов?
Что такое антипаттерн, с примерами из автоматизации тестов?
Антипаттерн — это распространённое, но контрпродуктивное решение, которое выглядит полезным, но создаёт проблемы. В автоматизации тестов: жёстко заданные ожидания (Thread.sleep), взаимозависимые тесты, требующие порядка, скопированный код тестов, оставленные в наборе flaky-тесты и пирамида-рожок (слишком много медленных UI-тестов, слишком мало unit-тестов).
Типичные ошибки
- ✗Принимать антипаттерн за лучшую практику
- ✗Не узнавать ожидания
Thread.sleepкак антипаттерн - ✗Думать, что пирамида-рожок желательна
Уточняющие вопросы
- →Почему пирамида-рожок является антипаттерном?
- →Чем заменить жёстко заданное ожидание
Thread.sleep?
MiddleТеорияИногдаЧто должен знать тестировщик про Git и что такое Git Flow?
Что должен знать тестировщик про Git и что такое Git Flow?
Ветка (branch) — это независимая линия разработки. Базовые команды: git clone копирует репозиторий, git pull получает обновления, git commit фиксирует изменения, git push загружает их. Git Flow — это модель ветвления с долгоживущими main и develop плюс короткоживущими feature/*, release/* и hotfix/*, дающая структурированный путь от работы к релизу.
Типичные ошибки
- ✗Путать, что делают
git pushиgit pull - ✗Думать, что в Git нет веток или только одна
- ✗Путать роли веток Git Flow (feature против hotfix)
Уточняющие вопросы
- →Зачем тестировщику переключаться на feature-ветку перед её тестом?
- →Какова цель hotfix-ветки в Git Flow?
MiddleТеорияИногдаВ фреймворке юнит-тестов чем отличается setup перед каждым тестом от setup один раз на класс?
В фреймворке юнит-тестов чем отличается setup перед каждым тестом от setup один раз на класс?
Hook перед каждым тестом (в JUnit @Before / @BeforeEach) выполняется перед каждым тест-методом, давая каждому тесту свежее изолированное состояние — для дешёвых изменяемых фикстур. Hook на класс (@BeforeClass / @BeforeAll) выполняется один раз перед всеми тестами класса и должен быть static, используется для дорогой общей подготовки вроде открытия соединения с БД. Правило: перед каждым — ради изоляции, на класс — для дорогих ресурсов, которые можно безопасно делить только на чтение.
Типичные ошибки
- ✗Менять местами, какой hook на тест, а какой на класс
- ✗Забывать, что hook на класс должен быть static
- ✗Делить изменяемое состояние в hook на класс, ломая изоляцию
Уточняющие вопросы
- →Почему hook на класс должен быть static в JUnit 4?
- →Когда общий ресурс на класс приведёт к flaky-тестам?
MiddleТеорияИногдаЧто такое мутационное тестирование и что оно измеряет?
Что такое мутационное тестирование и что оно измеряет?
Мутационное тестирование вносит маленькие намеренные дефекты (мутанты) в код — меняет оператор, меняет константу — затем перезапускает набор тестов. Если тест падает, мутант убит; если все тесты проходят, мутант выжил, обнажая пробел. Оно измеряет качество самих тестов (правда ли они ловят баги), идя дальше покрытия строк, которое лишь показывает, что код выполнился.
Типичные ошибки
- ✗Думать, что оно мутирует тестовые данные, а не код
- ✗Считать, что выживший мутант означает хорошие тесты
- ✗Приравнивать его к покрытию строк
Уточняющие вопросы
- →Почему покрытие строк — более слабый сигнал качества, чем доля убитых мутантов?
- →Что выживший мутант говорит вам добавить в тесты?
MiddleТеорияИногдаКак ускорить прогон тестов и чем параллельный отличается от распределённого?
Как ускорить прогон тестов и чем параллельный отличается от распределённого?
Ускоряют запуском тестов конкурентно, отсечением избыточных, мокированием медленных зависимостей и переносом проверок на более низкие (быстрые) уровни. Параллельный прогон запускает один набор конкурентно, часто на нескольких устройствах или браузерах; распределённый шардирует набор по узлам, так что разные тесты идут на разных машинах, сокращая общее время по часам.
Типичные ошибки
- ✗Менять местами определения параллельного и распределённого
- ✗Думать, что добавление sleep ускоряет прогон
- ✗Забывать, что перенос проверок на низкие уровни быстрее
Уточняющие вопросы
- →Какие проблемы состояния вскрывает параллельный прогон?
- →Когда шардирование (распределённый) помогает больше, чем параллельность?
MiddleТеорияИногдаЧем различаются нагрузочное тестирование, стресс-тестирование и синтетический мониторинг?
Чем различаются нагрузочное тестирование, стресс-тестирование и синтетический мониторинг?
Нагрузочное тестирование проверяет поведение под ожидаемой реалистичной нагрузкой, подтверждая, что система достигает целей (время отклика, пропускная способность) при обычном и пиковом трафике. Стресс-тестирование выходит за эти пределы, чтобы найти точку отказа и увидеть, как система падает и восстанавливается. Синтетический мониторинг гоняет скриптованные транзакции по расписанию против прода, наблюдая доступность и производительность во времени. Нагрузка проверяет ёмкость, стресс ищет пределы, синтетика наблюдает постоянно — это не предрелизный гейт.
Типичные ошибки
- ✗Менять местами цели нагрузочного и стресс-тестирования
- ✗Считать синтетический мониторинг одноразовым предрелизным тестом
- ✗Игнорировать время отклика и пропускную способность как метрики
Уточняющие вопросы
- →Какие метрики подтверждают прохождение нагрузочного теста?
- →Почему гонять синтетический мониторинг именно против прода?
SeniorДизайнИногдаНужно найти, какая версия внесла баг. Известно, что ранняя сборка работает, а более поздняя содержит баг; между ними много сборок. Спроектируйте, как эффективно локализовать виновную версию. Объясните, почему не стоит тестировать версии подряд одну за другой, какую стратегию поиска примените и её стоимость и какая доп. информация (например, баг в коде или в данных) позволит сузить поиск быстрее.
Нужно найти, какая версия внесла баг. Известно, что ранняя сборка работает, а более поздняя содержит баг; между ними много сборок. Спроектируйте, как эффективно локализовать виновную версию. Объясните, почему не стоит тестировать версии подряд одну за другой, какую стратегию поиска примените и её стоимость и какая доп. информация (например, баг в коде или в данных) позволит сузить поиск быстрее.
Используйте бинарный поиск (git bisect): раз за разом тестируйте сборку в середине подозрительного диапазона, оставляя только ту половину, где сохраняется переход от рабочей к сломанной. Это O(log n) проверок против O(n) при тесте каждой версии подряд. Сузьте дальше, сперва выяснив, баг в коде или в данных — это может исключить целый класс сборок до бисекта.
Типичные ошибки
- ✗Тестировать версии линейно вместо бисекта
- ✗Называть бинарный поиск O(n) вместо O(log n)
- ✗Игнорировать, баг в коде или в данных
Уточняющие вопросы
- →Сколько проверок займёт бисект 1024 сборок против линейного обхода?
- →Почему знание, что баг в данных, сужает поиск?
SeniorДизайнИногдаВаши автотесты идут на чистой базе, и один тест заказывает пиццу — но ему нужна открытая пиццерия, а набор может запускаться ночью, когда она закрыта. Тест зависит от внешнего реального состояния, которое вы не контролируете. Как сделать этот тест надёжным? Разберите варианты обеспечения нужного предусловия, какой предпочтёте и почему, и как это обобщается на любой тест, зависящий от времени или внешнего состояния.
Ваши автотесты идут на чистой базе, и один тест заказывает пиццу — но ему нужна открытая пиццерия, а набор может запускаться ночью, когда она закрыта. Тест зависит от внешнего реального состояния, которое вы не контролируете. Как сделать этот тест надёжным? Разберите варианты обеспечения нужного предусловия, какой предпочтёте и почему, и как это обобщается на любой тест, зависящий от времени или внешнего состояния.
Никогда не завязывайтесь на неконтролируемое реальное состояние. Лучше: замокать часы или флаг открыто/закрыто, чтобы предусловие всегда выполнялось детерминированно. Иначе засеять базу в известное открытое состояние или вызвать API в setup теста для подготовки данных. Общее правило: тест должен владеть своими предусловиями — контролируйте время и внешнее состояние через моки, seeding или setup, а не живое окружение.
Типичные ошибки
- ✗Позволять тесту зависеть от неконтролируемого реального состояния
- ✗Добавлять sleep или перезапуски вместо контроля предусловия
- ✗Удалять проверку вместо владения предусловием
Уточняющие вопросы
- →Почему замокать часы здесь предпочтительнее, чем засеять базу?
- →Как этот принцип применяется к тесту, зависящему от стороннего API?
SeniorДебаггингРедкоМессенджер показывает «Не отправлено» на файле — баг или нет?
Мессенджер показывает «Не отправлено» на файле — баг или нет?
Это не баг, если отказ — корректная обработка реального условия — нет сети, файл слишком большой или неподдерживаемого типа, сервер недоступен, истёкшая сессия — и пользователю ясно сказано почему и он может повторить. Это баг, если отказ при валидных условиях, без причины, без повтора или сообщение неверное либо вводит в заблуждение. Проверка — ожидаемое против фактического плюс корректная деградация.
Открыть задачу →Типичные ошибки
- ✗Заводить любое сообщение об ошибке как баг без проверки условия
- ✗Игнорировать, показаны ли причина и повтор
- ✗Пропускать суждение ожидаемое против фактического
Уточняющие вопросы
- →Что превратит корректный отказ в баг для заведения?
- →Почему ясная причина плюс повтор — граница между багом и не-багом здесь?
SeniorДебаггингРедкоПриложение заявок показывает «Ошибка сохранения» — диагностируйте по цепочке
Приложение заявок показывает «Ошибка сохранения» — диагностируйте по цепочке
Сначала соберите данные: откройте DevTools для разбора запроса/ответа, включите прокси, прочитайте логи приложения и отладьте бэкенд, если есть доступ. Затем рассуждайте по всей цепочке: дубликат номера заявки, кириллический символ от генератора, серверная валидация, отклоняющая номер или текст, упавшая БД, отказ сети, медленный/истёкший ответ или некорректный запрос от фронта.
Открыть задачу →Типичные ошибки
- ✗Прыгать к одной причине без сбора данных запроса/ответа
- ✗Игнорировать целые звенья цепочки (сеть, БД, валидация)
- ✗Считать, что номер от генератора всегда валиден
Уточняющие вопросы
- →Как статус-код ответа сузит, какое звено отказало?
- →Какие причины прокси даст подтвердить там, где DevTools одних мало?
SeniorДебаггингРедкоОдин URL отдаёт разным устройствам разные версии файла — почему?
Один URL отдаёт разным устройствам разные версии файла — почему?
Вероятные причины: устаревший edge-кэш CDN (один узел держит старую версию), гео- или локале-зависимое согласование контента (заголовки Accept-Language или гео выбирают версию), устаревший локальный кэш браузера или VPN, меняющий выходной узел. Расследуйте, разбирая заголовки запроса/ответа в DevTools или прокси и сравнивая ETag, Last-Modified и Cache-Control между устройствами.
Типичные ошибки
- ✗Считать, что один URL всегда возвращает те же байты
- ✗Игнорировать кэш CDN и гео/локале-согласование контента
- ✗Не сравнивать заголовки
ETag/Last-Modified/Cache-Control
Уточняющие вопросы
- →Какой заголовок ответа выдаст устаревший edge-узел CDN?
- →Как
Accept-Languageможет дать одному устройству старую сборку?