Перейти к содержимому

Вопросы по тестированию на собеседовании: что спрашивают и как отвечать

Команда Interview Assistantтехнические вопросытестирование

На собеседовании по тестированию проверяют не знание названий фреймворков, а понимание того, зачем тесты нужны и сколько они стоят: какие уровни тестов бывают, чем unit-тест отличается от интеграционного, когда заменять зависимость моком, а когда поднимать настоящую базу, и почему высокий процент покрытия ещё не гарантирует качество. Сильный ответ опирается на компромисс между скоростью, надёжностью и уверенностью, которую даёт тест, и подкрепляется примером из собственной практики. Ниже разобраны главные темы, формулировки ответов коротко и развёрнуто и ловушки, на которых кандидаты теряют баллы.

Что на самом деле проверяет интервьюер

Вопросы про тесты звучат почти на каждом техническом собеседовании, и за ними стоят несколько разных проверок:

  • Пишете ли вы тесты на практике. Тот, кто тестирует регулярно, говорит конкретно: что именно проверял, какие тесты ломались и почему. Тот, кто знает тему по статьям, перечисляет определения.
  • Понимаете ли вы цену тестов. Каждый тест нужно писать, запускать и поддерживать. Интервьюер хочет услышать, что вы умеете выбирать уровень теста под задачу, а не покрывать всё подряд e2e-сценариями.
  • Пишете ли вы тестируемый код. Вопрос про моки часто оказывается вопросом про архитектуру: внедрение зависимостей, отделение бизнес-логики от ввода-вывода, чистые функции.
  • Как вы относитесь к качеству в команде. Что делаете с нестабильным тестом, как ревьюите тесты коллег, блокирует ли красный CI мерж.

На live coding тема всплывает практически: после решения задачи могут попросить назвать тест-кейсы или написать пару тестов. Отдельные тесты к тестовому заданию почти всегда ставят плюсом.

Пирамида тестов

Пирамида — классическая модель распределения тестов по уровням: внизу много быстрых и дешёвых тестов, наверху мало медленных и дорогих. Вопросы по тестам обычно идут рядом с языковыми, например в собеседовании Python для backend-разработчика.

УровеньЧто проверяетСкоростьСтабильностьСколько таких тестов
UnitОтдельную функцию или класс в изоляцииМиллисекундыВысокаяБольше всего
IntegrationВзаимодействие компонентов: код с базой, очередью, HTTP-клиентомСекундыСредняяМеньше
E2EСценарий пользователя через всю систему, часто через UI или публичный APIСекунды и минутыНиже всехНемного, только ключевые сценарии

Коротко: чем выше уровень, тем больше уверенности даёт тест, но тем он медленнее, дороже в поддержке и чаще ломается по причинам, не связанным с кодом.

Развёрнуто: пирамида — ориентир, а не закон. В сервисе, который в основном перекладывает данные между базой и API, интеграционные тесты полезнее сотни unit-тестов с моками; отсюда альтернативные модели вроде «тестового трофея». Антипаттерн, который стоит назвать по имени, — «рожок мороженого»: много ручных и e2e-проверок, мало тестов нижнего уровня. Такой набор медленный, нестабильный и плохо локализует ошибку.

Unit, integration и e2e: где граница

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

  • Классическая (детройтская): unit — это единица поведения, а не обязательно один класс. Тест может задействовать несколько реальных классов, если они не ходят во внешний мир. Подменяют только то, что медленно или недетерминированно: сеть, базу, время, файловую систему.
  • Мокистская (лондонская): unit — это один класс, все его зависимости заменяются тест-дублями, а тест часто проверяет, какие вызовы класс сделал.

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

Интеграционный тест проверяет стык с реальной инфраструктурой. Хороший пример — репозиторий, который тестируется против настоящей PostgreSQL в контейнере, а не против мока: только так ловятся ошибки в SQL, миграциях и ограничениях схемы. Если в вакансии есть работа с базой, пригодится и статья про вопросы по SQL.

Моки, стабы и другие тест-дубли

Здесь интервьюеры любят проверять точность терминов. Общее название — тест-дубль (test double), а виды различаются назначением:

ВидЧто делаетПример
DummyПередаётся, но не используетсяПустой логгер в конструкторе
StubВозвращает заранее заданные ответыКурс валют всегда 100
FakeРабочая упрощённая реализацияРепозиторий на словаре в памяти
SpyЗапоминает, как его вызывалиСчётчик отправленных писем
MockЗаранее настроен ожиданиями вызовов и проверяет их«Метод send должен быть вызван один раз с этим адресом»

Коротко: стаб отвечает на вопросы теста и нужен, чтобы проверить состояние; мок проверяет взаимодействие, то есть сам факт и параметры вызова.

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

Пример, который хорошо показать на доске: время — тоже зависимость. Функцию, которая внутри читает текущее время, трудно протестировать; если передать время параметром, тест становится тривиальным.

from datetime import date

def is_expired(expires_on: date, today: date) -> bool:
    return today > expires_on

def test_subscription_expires_next_day():
    assert not is_expired(date(2026, 1, 31), today=date(2026, 1, 31))
    assert is_expired(date(2026, 1, 31), today=date(2026, 2, 1))

Тест проверяет границу и не зависит от дня запуска.

TDD

Коротко: TDD — практика, при которой тест пишется до кода короткими циклами red — green — refactor: написать падающий тест, написать минимальный код, чтобы он прошёл, затем улучшить код, сохраняя тесты зелёными.

Развёрнуто: главная польза TDD не в количестве тестов, а в дизайне. Код, который пишется под тест, сразу получается тестируемым: с явными зависимостями и маленькими функциями. Честный ответ о своей практике ценится выше показной приверженности. Нормально сказать: «Строго по TDD я пишу сложную бизнес-логику и исправления багов: сначала тест, воспроизводящий баг, потом фикс. Для прототипов и кода, где ещё неясны требования, сначала пишу код, а тесты добавляю, когда форма устоялась». Тест, воспроизводящий баг, защищает от его возвращения.

Flaky-тесты

Flaky-тест иногда проходит, иногда падает без изменений в коде. Это популярный вопрос для middle и выше, потому что он про реальную боль.

Типичные причины:

  • зависимость от времени, часового пояса или порядка выполнения тестов;
  • общее изменяемое состояние между тестами: глобальные переменные, общая база без очистки;
  • асинхронность и гонки: тест проверяет результат раньше, чем он появился, или использует фиксированный sleep;
  • внешние сервисы и сеть;
  • недетерминированный порядок, например при итерации по множеству или сортировке без полного ключа.

Сильный ответ описывает процесс: воспроизвести падение многократным прогоном, временно изолировать тест с задачей на исправление, найти источник недетерминизма и устранить его. sleep заменяется ожиданием условия с таймаутом. Автоперезапуск упавших тестов допустим как временная мера; если он становится нормой, команда перестаёт доверять CI.

Покрытие кода

Коротко: покрытие показывает, какие строки или ветки выполнились во время тестов, но не показывает, проверили ли тесты результат.

Развёрнуто: тест без единого assert может дать высокое покрытие и не поймать ничего. Покрытие полезно для поиска непроверенного кода, а не как цель: жёсткий порог в CI провоцирует тесты ради цифры. Хорошо упомянуть разницу между покрытием строк и покрытием веток, а также мутационное тестирование: инструмент вносит в код небольшие изменения (меняет > на >=, удаляет вызов) и проверяет, упадут ли тесты. Если мутант выжил, тесты этот участок по-настоящему не проверяют.

Вопрос — слабый ответ — сильный ответ

ВопросСлабый ответСильный ответ
Зачем нужны тесты?«Чтобы не было багов»Быстрая обратная связь, безопасный рефакторинг, документация поведения, защита от регрессий
Какое покрытие нужно?«Сто процентов»Цифра вторична; важно, покрыта ли критичная логика и граничные случаи
Мок или настоящая база?«Всегда мок, так быстрее»Логику — с дублями, слой доступа к данным — против настоящей базы в контейнере
Что делать с flaky-тестом?«Перезапустить»Изолировать, воспроизвести, найти недетерминизм, исправить причину
Какие тест-кейсы у этой функции?Один позитивный сценарийОбычный случай, границы, пустой ввод, некорректные данные, ошибки зависимостей

Типичные ошибки и ловушки

  • Тестировать реализацию, а не поведение: проверять приватные методы и порядок внутренних вызовов.
  • Мокать то, чем вы не владеете, вместо того чтобы обернуть внешнюю библиотеку своим интерфейсом и тестировать адаптер интеграционно.
  • Несколько несвязанных проверок в одном тесте, после падения которого непонятно, что сломалось.
  • Тесты, зависящие друг от друга или от порядка запуска.
  • Называть только позитивные сценарии, когда просят придумать тест-кейсы. Интервьюер ждёт границ, пустых значений и ошибок.

Чек-лист подготовки

  1. Пирамида тестов, её альтернативы и антипаттерн «рожок мороженого».
  2. Чем unit отличается от integration и e2e, две школы unit-тестирования.
  3. Виды тест-дублей, разница проверки состояния и поведения.
  4. Цикл TDD и честная история о том, как вы применяете его сами.
  5. Причины flaky-тестов и процесс их устранения.
  6. Что показывает и чего не показывает покрытие, идея мутационного тестирования.
  7. Структура теста Arrange — Act — Assert и понятные имена тестов.
  8. Одна история из практики: тест, который поймал серьёзную ошибку, или баг, который тесты пропустили, и что вы изменили после этого.

Если на интервью вы проектируете систему, вопрос «как вы будете её тестировать» уместно задать себе самому: это хорошо смотрится и на секции system design.

Где здесь помогает Interview Assistant

Вопросы про тестирование часто звучат как открытые: «расскажите, как вы тестируете сервис» или «какие тесты напишете к этой функции». Interview Assistant — приложение для macOS и Windows, которое транскрибирует речь интервьюера в реальном времени и отвечает сначала краткими тезисами, а затем развёрнутым разбором с подводными камнями: например, напомнит про граничные случаи или разницу между моком и стабом. Ассистент не заменяет знания и собственный опыт, но помогает не упустить важный пункт под волнением. Первые 20 минут бесплатны — можно заранее проверить, как приложение слышит ваш звонок.

Частые вопросы

Нужно ли бэкенд-разработчику знать e2e-тестирование?

На уровне идей — да: что такое e2e, почему таких тестов мало и почему они нестабильны. Писать браузерные сценарии обычно ждут от фронтенда и QA-автоматизаторов, а от бэкенда чаще ждут интеграционных тестов API и слоя данных.

Что ответить, если в прошлой команде тестов почти не было?

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

Чем интеграционный тест отличается от e2e?

Интеграционный тест проверяет стык нескольких компонентов, например код и базу, остальное может быть подменено. E2E проверяет пользовательский сценарий через всю развёрнутую систему, от входа до результата. Граница размыта, так что поясните, что понимаете под каждым термином.

Обязательно ли знать TDD?

Знать идею и цикл red — green — refactor нужно. Применять TDD всегда никто не требует: важнее объяснить, где он полезен, и рассказать о своей реальной практике.

Как придумывать тест-кейсы к функции на собеседовании?

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

Сколько тестов должно быть в проекте?

Конкретной нормы нет. Ориентир — уверенность: можно ли смело менять код и выкатывать его, не проверяя всё руками. Если команда боится трогать модуль, тестов там не хватает, даже если общий процент покрытия высокий.