Фікстури (fixtures) у Pytest — це потужний і зручний механізм для підготовки даних або стану оточення перед виконанням тестів. Простими словами, фікстури відповідають на два важливі питання:
- Звідки взяти дані для тестів?
- Як налаштувати "поляну" для тестування?
Навіщо потрібні фікстури?
- Автоматизація рутинних задач: повторюваний код по ініціалізації даних винесено в одне місце.
- Організація тестів: кожна фікстура виконує одну чітку задачу, покращуючи читабельність і підтримку тестів.
- Повторне використання: одна фікстура може бути використана в кількох тестах одразу.
- Швидкий старт: навіть якщо тестів десятки чи сотні, підготовка оточення відбувається без зайвих зусиль.
Базове створення фікстур
Фікстури в Pytest — це функції, позначені декоратором @pytest.fixture. Давай почнемо з простого прикладу.
Приклад 1: фіксовані дані
Припустимо, у нас є API для керування користувачами. Ми пишемо тест для реєстрації нового користувача. Щоб не створювати дані вручну кожного разу, визначимо фікстуру user_data:
# tests/conftest.py
import pytest
@pytest.fixture
def user_data():
"""Фікстура для створення тестових даних користувача"""
return {"username": "test_user", "password": "secure_password"}
Тепер ми можемо використовувати цю фікстуру в будь-якому тесті:
# tests/test_users.py
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_user_registration(user_data):
"""Тестуємо реєстрацію нового користувача"""
response = client.post("/register", json=user_data)
assert response.status_code == 201
assert response.json()["username"] == user_data["username"]
Коли ти вказуєш назву фікстури (user_data) в аргументах функції тесту, Pytest автоматично викликає цю фікстуру і передає результат в тест.
Приклад 2: підключення до бази даних
Дуже часто фікстури використовують для підготовки складного оточення — наприклад, підключення до бази даних.
# tests/conftest.py
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from myapp.database import Base
DATABASE_URL = "sqlite:///./test.db"
@pytest.fixture(scope="session")
def db_engine():
"""Фікстура для створення движка бази даних"""
engine = create_engine(DATABASE_URL)
Base.metadata.create_all(bind=engine)
yield engine
Base.metadata.drop_all(bind=engine)
@pytest.fixture
def db_session(db_engine):
"""Фікстура для створення сесії бази даних"""
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=db_engine)
session = SessionLocal()
yield session
session.close()
Тут ми:
- Створюємо движок бази даних один раз на весь тестовий запуск завдяки
scope="session". - Створюємо і підготовлюємо таблиці перед стартом тестів.
- Прибираємо таблиці після завершення всіх тестів (cleanup).
- Генеруємо окремі сесії бази даних для кожного тесту.
Приклад 3: використання кількох фікстур
Фікстури можна комбінувати. Наприклад, якщо нам потрібна база даних і заздалегідь створений користувач:
# tests/conftest.py
@pytest.fixture
def test_user(db_session):
"""Створення тестового користувача в базі даних"""
new_user = User(username="test_user", password="secure_password")
db_session.add(new_user)
db_session.commit()
return new_user
Тепер ми можемо використовувати test_user в будь-якому тесті:
# tests/test_users.py
def test_get_user(test_user, client):
response = client.get(f"/users/{test_user.id}")
assert response.status_code == 200
assert response.json()["username"] == test_user.username
Приклад 4: параметризовані фікстури
Інколи хочеться протестувати той самий endpoint з різними варіаціями даних. Для цього Pytest пропонує параметризовані фікстури.
# tests/conftest.py
@pytest.fixture(params=[
{"username": "user1", "password": "pass1"},
{"username": "user2", "password": "pass2"},
{"username": "user3", "password": "pass3"}
])
def user_data(request):
"""Фікстура з різними наборами даних"""
return request.param
Тепер кожен виклик тесту буде використовувати новий набір даних:
# tests/test_users.py
def test_multiple_users(user_data, client):
response = client.post("/register", json=user_data)
assert response.status_code == 201
assert response.json()["username"] == user_data["username"]
Поради і трюки для роботи з фікстурами
- Використовуй файл
conftest.py: Помісти часто вживані фікстури в файлtests/conftest.py, щоб вони автоматично підвантажувалися в усіх тестах. - Налаштуй
scope: Фікстури можна виконувати для:function(за замовчуванням) — для кожного тесту.class— для всіх тестів всередині тестового класу.module— для всіх тестів всередині файлу.session— для всіх тестів одного запуску.
- Використовуй
yield: Для дій, що вимагають "прибиральних" операцій після тестів (наприклад, видалення даних з бази), використовуйyieldдля розділення етапів setup і teardown. - Імпорти всередині фікстур: Якщо фікстура використовує рідко вживаний модуль, імпортуй його всередині самої функції, щоб не вантажити зайву пам'ять під час запуску тестів.
Типові помилки при роботі з фікстурами
Давай розберемося, з якими проблемами ти можеш зіткнутися:
- Забув вказати фікстуру в аргументах тесту. Наприклад, ти визначив фікстуру, але не використав її в тесті. Pytest просто її проігнорує.
- Неправильне використання
scope. Якщо ти налаштував фікстуру якsession, а дані в ній не є "потокобезпечними", це може викликати несподівані баги. - Неправильне завершення роботи фікстур. Якщо забути видалити тимчасові дані або закрити з'єднання, це може призвести до "витоків" в тестовому оточенні.
Резюме
Фікстури Pytest значно спрощують життя розробникам, позбавляючи від рутинного створення даних і налаштування оточення. На наступній лекції ми розберемо запуск тестів і генерацію звітів про покриття, щоб ти зміг оцінити, наскільки якісно покритий твій код. А поки використовуй фікстури і насолоджуйся магією автоматизації!
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ