Вопросы по тестированию в Python на собеседовании
Тесты спрашивают, чтобы понять, как вы работаете каждый день. Разговор обычно уходит от «что такое юнит-тест» к практике: как изолировать внешний сервис, что делать с общей фикстурой и почему высокое покрытие не гарантирует качества.
Из чего состоит тема
Так тема разложена в тренажёре: движок ведёт прогресс по каждой подтеме отдельно и возвращает те, где вы ошибаетесь.
- TDD и покрытие14
- Async-тесты12
- pytest: основы12
- Фикстуры и моки12
- Виды тестов и дублёры11
- Тестовые данные и контейнеры11
Разборы подтем
Конспект по каждой: что это, как отвечать вслух, на чём валятся, плюс вопросы для самопроверки.
- Тестирование асинхронного кода12 вопросов
- Фикстуры и моки в pytest12 вопросов
- Pytest: основы12 вопросов
- TDD и покрытие тестами14 вопросов
- Тестовые данные и контейнеры11 вопросов
- Виды тестов и дублёры в Python11 вопросов
Примеры вопросов с разбором
- Обычная тест-функция не умеет await. Как pytest прогнать async-тест?A)Плагином pytest-asyncio с маркером @pytest.mark.asyncio на async def тестеB)Никак: pytest в принципе не поддерживает асинхронные тесты ни через какие плагиныC)Обернуть тело теста в asyncio.run вручную внутри обычной синхронной тест-функцииD)Достаточно объявить тест как async def — pytest сам исполнит корутину без плагинов
показать ответ и разбор
+A)Плагином pytest-asyncio с маркером @pytest.mark.asyncio на async def тесте// разбор: pytest сам не крутит корутины: async def тест без поддержки просто не выполнится (будет предупреждение о невостребованной корутине). Плагин pytest-asyncio даёт цикл событий и исполняет async-тесты, помеченные @pytest.mark.asyncio (или через режим auto). Альтернатива — anyio-плагин. Так внутри теста можно спокойно делать await.
- Что такое фикстура в pytest?A)Функция, готовящая зависимость и отдающая её тестуB)Файл с тестовыми данными в формате JSON, лежащий рядом с тестамиC)Заранее зафиксированный ожидаемый результат теста для сравненияD)Специальный тест, который обязательно запускается перед всеми остальными
показать ответ и разбор
+A)Функция, готовящая зависимость и отдающая её тесту// разбор: Фикстура — функция с @pytest.fixture, которая готовит и отдаёт зависимость (соединение с БД, клиент, временные данные). Тест получает её, просто объявив имя фикстуры в параметрах — pytest подставит результат. Так инъекция зависимостей и переиспользование setup встроены в фреймворк.
- Как pytest по умолчанию находит тесты в проекте?A)Файлы test_*.py и функции/методы, начинающиеся с test_B)Ищет только классы, унаследованные от специального базового TestCaseC)По списку тестов, который надо явно перечислить в конфиге проектаD)Запускает вообще все функции во всех модулях подряд без разбора
показать ответ и разбор
+A)Файлы test_*.py и функции/методы, начинающиеся с test_// разбор: pytest собирает тесты по соглашению: модули test_*.py (или *_test.py), функции test_* и методы test_* в классах Test*. Ни регистрировать, ни наследоваться от базового класса не нужно — в этом отличие от unittest. Пути и шаблоны настраиваются в pytest.ini/pyproject.
- Каков цикл TDD?A)Refactor — Green — Red: сначала чистим код, потом ломаем тестомB)Написать весь код, затем покрыть его тестами и отрефакторить в концеC)Red — Green — Refactor: падающий тест, минимум кода, чисткаD)Написать тесты, прогнать один раз и больше к ним не возвращаться
показать ответ и разбор
+C)Red — Green — Refactor: падающий тест, минимум кода, чистка// разбор: TDD: сначала пишут падающий тест на желаемое поведение (Red), затем минимальный код, чтобы он позеленел (Green), затем рефакторят реализацию, держа тест зелёным (Refactor) — и повторяют мелкими шагами. Тест ведёт дизайн: сначала контракт, потом реализация.
- Зачем в тестах factory_boy и faker?A)Заменять базу данных на время теста своей реализацией в памятиB)Генерировать валидные тестовые объекты без ручного заполнения полейC)Ускорять сами тесты за счёт кэширования результатов между прогонамиD)Автоматически находить баги в коде без написания ассертов вручную
показать ответ и разбор
+B)Генерировать валидные тестовые объекты без ручного заполнения полей// разбор: factory_boy описывает, как собрать валидный объект (модель) с разумными дефолтами, а faker подставляет правдоподобные значения (имена, email, даты). Вместе они убирают простыни ручного заполнения полей и «магические» константы, оставляя в тесте только то, что для него существенно.
- Чем юнит-тест отличается от интеграционного в бэкенде на Python?A)Интеграционный тест проверяет одну функцию, а юнит — весь сервис целикомB)Юнит изолирует логику моками, интеграционный бьёт по реальным зависимостямC)Юнит-тесты пишут на pytest, а интеграционные — только на unittestD)Юнит-тест обязательно медленнее интеграционного из-за настройки моков
показать ответ и разбор
+B)Юнит изолирует логику моками, интеграционный бьёт по реальным зависимостям// разбор: Юнит-тест проверяет единицу логики в изоляции, подменяя внешние зависимости (БД, HTTP) моками/фейками — быстро и точечно. Интеграционный проверяет связку с реальными зависимостями (настоящая БД в контейнере, соседний сервис) — медленнее, но ловит то, что моки скрывают: SQL, сериализацию, контракты.
- Как протестировать async-эндпоинт FastAPI, дергая приложение без реального сетевого сервера?A)httpx.AsyncClient поверх ASGITransport(app=app) — запросы идут прямо в приложениеB)Использовать синхронный requests, обернув каждый вызов в отдельный event loop вручнуюC)Поднять приложение через uvicorn на реальном порту и ходить к нему по сети из тестаD)Вызывать функции-обработчики напрямую, минуя маршрутизацию, валидацию и middleware
показать ответ и разбор
+A)httpx.AsyncClient поверх ASGITransport(app=app) — запросы идут прямо в приложение// разбор: Приложение FastAPI — ASGI-объект, поэтому его тестируют in-process: httpx.AsyncClient с transport=ASGITransport(app=app) шлёт запросы прямо в приложение, проходя маршрутизацию, валидацию и middleware, но без реального TCP-сервера. Это быстро и достоверно. Синхронный аналог — starlette TestClient. Прямой вызов функции-обработчика обходит весь стек и потому менее показателен.
- Фикстура с
scope="session"— как часто она создаётся?A)Только если тест явно попросит пересоздать её вручную флагомB)Один раз на весь прогон и переиспользуется всеми тестамиC)Заново на каждый тест, как обычная фикстура по умолчаниюD)По разу на каждый тестовый класс в наборе, но не на модульпоказать ответ и разбор
+B)Один раз на весь прогон и переиспользуется всеми тестами// разбор: scope задаёт время жизни фикстуры: function (по умолчанию, на каждый тест), class, module, session (один раз на весь прогон). session-фикстура создаётся единожды и переиспользуется — хороша для дорогого setup (поднять контейнер БД). Но общий изменяемый стейт в широком scope легко даёт связанность тестов.
- Почему в pytest пишут обычный
assert x == y, а не специальные assertEqual?A)pytest молча глотает обычный assert, поэтому он безопаснее прочихB)Обычный assert работает быстрее специальных методов проверкиC)pytest переписывает assert и показывает разбор значенийD)Специальные assert-методы в Python вообще запрещены синтаксисомпоказать ответ и разбор
+C)pytest переписывает assert и показывает разбор значений// разбор: pytest на этапе импорта переписывает assert (assertion rewriting): при падении он показывает фактические значения обеих сторон и по каким элементам они разошлись. Поэтому голого assert хватает — не нужны assertEqual/assertTrue из unittest. Понятная диагностика идёт из коробки.
это 9 из 72
Ещё 63 вопросов по теме — в тренажёре, с движком повторения
Прочитать разбор и ответить самому — разные навыки. В Сеньорчике вопросы идут сессиями, а движок возвращает подтемы, где вы ошибаетесь, пока они не начнут отскакивать. Бесплатно, лимит по энергии.
Частые вопросы
Что спрашивают про фикстуры pytest?
Области видимости и порядок выполнения, как передавать данные между тестами, чем фикстура лучше setUp и когда её стоит заменить обычной функцией.
Как отвечать про покрытие тестами?
Покрытие показывает, какой код выполнялся, но не проверяет корректность утверждений. Хороший ответ отделяет метрику от цели и говорит, где нужны интеграционные проверки.