Все разборы

Автотесты

Как ответить на вопрос «Что такое фикстуры в pytest» на собеседовании QA

Как рассказать про фикстуры pytest на собеседовании: уборка через yield, conftest.py, scope от function до session и его связь с независимостью тестов.

Зачем это спрашивают

Фикстуры — основа любого проекта автотестов на pytest. Через них готовят данные, создают клиентов API, открывают браузер и убирают за собой. Интервьюер спрашивает про них, чтобы понять, писали ли вы тесты сами или только запускали чужие. По ответу видно, знаете ли вы, где держать общую подготовку, как не оставлять мусор на стенде и почему тесты не должны зависеть друг от друга.

Обычно за первым вопросом сразу идут уточнения: как сделать уборку после теста и что такое scope. Готовьтесь отвечать на все три части сразу.

Как построить сильный ответ

Определение

Фикстура — функция с декоратором @pytest.fixture, которая готовит что-то для теста: данные, объект, соединение. Тест получает результат фикстуры, если указывает её имя в параметрах. pytest сам находит фикстуру, вызывает её до теста и передаёт значение.

Подготовка и уборка через yield

Если после теста нужно что-то убрать, фикстура пишется через yield. Код до yield — подготовка, значение после yield получает тест, код после — уборка. Уборка выполняется после теста, даже если тест упал на assert. Так созданные данные не копятся на стенде.

import pytest
import requests

BASE_URL = "http://localhost:8000"


@pytest.fixture
def api():
    session = requests.Session()
    yield session
    session.close()


@pytest.fixture
def note(api):
    resp = api.post(f"{BASE_URL}/api/notes", json={"text": "черновик"})
    resp.raise_for_status()
    created = resp.json()
    yield created
    api.delete(f"{BASE_URL}/api/notes/{created['id']}")


def test_note_is_draft(note):
    assert note["status"] == "draft"

Пример условный: сервис заметок с методами создания и удаления. Здесь видно ещё одно свойство: фикстура note сама зависит от фикстуры api. Фикстуры складываются в цепочки, и тест запрашивает только то, что ему нужно.

conftest.py

Общие фикстуры кладут в файл conftest.py. pytest подхватывает его автоматически, импортировать ничего не нужно. Фикстуры из conftest.py видны всем тестам в этой папке и вложенных.

Scope

Scope задаёт, как часто фикстура создаётся заново:

scope когда создаётся
function для каждого теста, по умолчанию
class один раз на класс с тестами
module один раз на файл
package один раз на пакет
session один раз на весь прогон

session подходит для дорогого и неизменяемого: адрес стенда, конфиг, авторизованная сессия, запущенный браузер. Данные, которые тесты меняют, лучше создавать на каждый тест. Если два теста делят один заказ и первый меняет его статус, результат второго зависит от порядка запуска — это прямой путь к нестабильным тестам.

Что ещё можно упомянуть

  • autouse=True — фикстура применяется ко всем тестам в области видимости без явного запроса. Удобно, но прячет зависимости, пользоваться аккуратно.
  • Параметризованная фикстура (params=[...]) запускает зависящие от неё тесты по разу для каждого значения.
  • Встроенные фикстуры: tmp_path для временных файлов, monkeypatch для подмены переменных окружения и атрибутов.
  • Альтернатива yield — request.addfinalizer, но yield читается проще.

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

Фикстура в pytest — это функция с декоратором pytest.fixture, которая готовит что-то для теста: тестовые данные, клиента API, браузер. Тест получает её, просто указав имя в параметрах. Если нужна уборка, я пишу фикстуру через yield: до yield создаю, например, заказ через API, отдаю его тесту, а после yield удаляю. Код после yield выполнится, даже если тест упал. Общие фикстуры держу в conftest.py, pytest находит его сам. У фикстуры есть scope: по умолчанию function, то есть заново на каждый тест, ещё бывают class, module, package и session. В session я выношу дорогое и неизменяемое — конфиг стенда, авторизацию. А данные, которые тест меняет, создаю на каждый тест, чтобы тесты не зависели друг от друга и от порядка запуска.

Частые ошибки

  • Уборка в конце тела теста. Если assert упадёт раньше, удаление не выполнится, и на стенде останется мусор.
  • Не знать про scope или считать, что фикстура по умолчанию создаётся один раз на прогон.
  • Делать изменяемые данные session-фикстурой «для скорости». Тесты начинают зависеть друг от друга и падают при смене порядка или параллельном запуске.
  • Импортировать фикстуры из conftest.py вручную. Это не нужно и запутывает проект.
  • Путать фикстуру с параметризацией тестов. @pytest.mark.parametrize перебирает входные данные, фикстура готовит окружение.

Пример на MarketCove

Допустим, вы пишете API-тесты для учебного магазина https://shop.tomiddle.com, Swagger лежит на /api/docs. Разумный набор фикстур мог бы выглядеть так:

  • base_url со scope session — адрес магазина берётся один раз из переменной окружения;
  • user_token со scope session или module — регистрация тестового пользователя через /api/shop/auth/* и получение токена; сам аккаунт тесты не меняют;
  • auth_client — сессия requests с заголовком авторизации;
  • order со scope function — свежий заказ через /api/shop/orders для каждого теста, который проверяет статусы или историю в /api/orders.

Поскольку заказы и аккаунты в магазине хранятся в памяти сервера, при перезапуске сервера данные пропадут. Поэтому фикстура не должна полагаться на заранее созданные заказы — каждый тест создаёт свои данные сам.

Задание для практики

Напишите conftest.py с тремя фикстурами: base_url (session), api (сессия requests, закрывается после использования) и product (берёт первый товар из /api/products). Затем напишите два теста, которые используют product, и запустите их с флагом --setup-show: pytest покажет, когда каждая фикстура создаётся и уничтожается. Поменяйте scope у product на module и посмотрите, как изменился вывод. Потом проверьте себя по вопросам в разделе про основы программирования.

Что спросят следом

  • Выполнится ли код после yield, если в самой фикстуре до yield произошла ошибка?
    Нет. Код после yield запускается, только если фикстура дошла до yield и отдала значение. Если подготовка упала раньше, тест получит ошибку на этапе setup, а уборка не начнётся. Поэтому в одной фикстуре лучше создавать один ресурс, а сложную подготовку разбивать на несколько фикстур.
  • Может ли фикстура с scope function использоваться в фикстуре с scope session?
    Нет, наоборот можно. Фикстура может зависеть только от фикстур с таким же или более широким scope. Если session-фикстура запросит function-фикстуру, pytest остановит тест с ошибкой ScopeMismatch, потому что объект на весь прогон не может опираться на объект, который живёт один тест.
  • Чем фикстура отличается от setup и teardown методов в стиле unittest?
    Фикстура подключается явно по имени параметра, поэтому видно, что нужно конкретному тесту. Фикстуры можно комбинировать, переиспользовать между файлами через conftest.py, параметризовать и настраивать время жизни через scope. Методы setup и teardown привязаны к классу и выполняются для всех его тестов одинаково.
  1. Q-01
    Что такое flaky-тесты и как с ними бороться?
  2. Q-02
    Что такое пирамида тестирования?
  3. Q-03
    Что вы проверяете при тестировании API?

Отрепетировать ответ с куратором

В сопровождении — весь банк вопросов, мок-собеседование и разбор каждого настоящего собеседования, пока не придёт оффер. Гарантий трудоустройства не даём — готовим к тому, что спросят.

Весь банк вопросов

Здесь — разбор одного вопроса. Банк целиком, с разбором и повторением, открыт в сопровождении; три вопроса — в витрине тренажёра.