Модуль 35

Фікстури та тестові бази даних

Ось урок, створений у стилі CS50: енергійно, з аналогіями та фокусом на розуміння суті.


🧠 CS50: Фікстури та Тестові Бази Даних

(або Як не спалити реальні дані під час експериментів)

Привіт, друзі! Це CS50, і сьогодні ми поговоримо про те, як спати спокійно, коли ваш код тестує фінансові транзакції або видаляє користувачів.


1. 🔥 Вступ: Чому ми взагалі про це говоримо?

Уявіть ситуацію. Ви пишете код для банку. Ваше завдання — написати функцію transfer_money(user_a, user_b, amount).

Ви хочете перевірити, чи вона працює. Що ви зробите? 1. Запустите код на реальній базі даних банку? 2. Переведете реальні гроші з рахунку вашої бабусі на рахунок Ілона Маска?

Звісно, ні! Це катастрофа. Якщо у коді помилка, бабуся залишиться без пенсії.

Але як тоді тестувати? Створити руками користувача «TestUser1»? Окей. Ви запустили тест, гроші перевелися. Запускаєте тест вдруге... Стоп! У «TestUser1» вже менше грошей. Тест впав, хоча код правильний. Просто дані змінилися.

Тут виникає проблема: Нам потрібен світ, де кожен тест починається з "чистого аркуша". Нам потрібна передбачуваність.

Уявіть це як зйомки бойовика 🎬. Коли режисер каже «Мотор!», каскадер стрибає крізь вікно. Вікно розбивається. Щоб зняти другий дубль, не можна використовувати розбите вікно. Декоратори миттєво ставлять нове, ціле вікно.

У програмуванні: * Сцена — це ваша Тестова База Даних. * Нове вікно (реквізит) — це Фікстури.


2. 🧠 Теорія: Що там «під капотом»?

Давайте розберемо це без складних термінів.

Що таке Тестова БД?

Це пісочниця. Окремий світ, який створюється спеціально для тестів. Коли ви запускаєте тести, ваша програма не підключається до production_db (де живуть реальні користувачі). Вона створює (або очищує) test_db.

Що таке Фікстура (Fixture)?

Фікстура — це фіксований стан даних, який ми завантажуємо в базу перед початком тесту.

Як це працює (Життєвий цикл): 1. Setup (Підготовка): Система створює порожню БД. 2. Load Fixtures: Магічним чином з’являються дані: «Користувач Андрій, баланс 100$». 3. Test (Дія): Ви викликаєте функцію transfer(Андрій, 50$). 4. Assert (Перевірка): Ви перевіряєте: чи стало в Андрія 50$? 5. Teardown (Прибирання): 💥 Система знищує цей світ. База даних очищується або видаляється.

❗️ Запам’ятайте головне: Тести мають бути ізольованими. Результат тесту А не повинен впливати на тест Б. Саме фікстури гарантують, що кожен тест стартує в ідеальних умовах.


3. 🧪 Приклади: Від простого до реального

Уявімо, що ми пишемо на Python (але логіка однакова для JS, Java, PHP).

Приклад 1: Проста фікстура (Словник)

Уявимо, що фікстура — це просто функція, яка повертає дані.

# Це наша фікстура
def get_test_user():
    return {"name": "Олена", "balance": 100, "status": "active"}

# Це наш тест
def test_bonus_calculation():
    user = get_test_user()  # Отримуємо свіжого юзера

    # Логіка: активним юзерам +10 бонусів
    if user["status"] == "active":
        user["balance"] += 10

    assert user["balance"] == 110  # Очікуємо 110

Питання до вас: Що буде, якщо я запущу цей тест 1000 разів підряд? Відповідь: Він 1000 разів пройде успішно. Бо get_test_user() щоразу повертає новий словник (або ми його щоразу заново запитуємо).


Приклад 2: Фікстура в Базі Даних (Як у реальному житті)

У реальних фреймворках (наприклад, Django, Pytest, Ruby on Rails) це виглядає трохи інакше. Ми кажемо системі: «Перед тестом завантаж ось цей файл у базу».

Файл users.json (наша фікстура):

[
  { "id": 1, "username": "alice", "role": "admin" },
  { "id": 2, "username": "bob", "role": "guest" }
]

Тест:

# Псевдокод
@use_fixtures("users.json")  # <--- Ось тут магія!
def test_admin_access():
    # На цей момент у ТЕСТОВІЙ базі вже є Alice і Bob.

    user = db.get_user(id=1)
    result = check_access(user)

    assert result == True  # Бо Alice - адмін

Чому результат саме такий? Тому що фреймворк тестування побачив @use_fixtures, відкрив тестову БД, залив туди дані з JSON і тільки потім передав керування у функцію test_admin_access.


4. 🛠 Практична частина

Час розім’яти пальці! Уявіть, що ви QA-інженер або Бекенд-розробник інтернет-магазину.

Завдання 1: «День бабака» Опишіть (словами або псевдокодом), що має статися в фазі Teardown (очищення), якщо ваш тест створював тимчасові файли на диску з чеками покупок?

Завдання 2: «Погана фікстура» Ви тестуєте функцію пошуку товарів. Ваша фікстура завантажує в базу 1 000 000 товарів. * У чому проблема такого підходу? * Як це виправити?

Завдання 3: Кейс «Залежність» У вас є два тести: 1. test_create_order: Створює замовлення №1. 2. test_delete_order: Намагається видалити замовлення №1. Розробник налаштував запуск тестів так, щоб вони йшли по черзі, і test_delete_order використовує те замовлення, яке створив перший тест. * Чому це жахлива ідея? (Підказка: що буде, якщо перший тест впаде з помилкою?)

Завдання 4: Реальна задача Вам треба протестувати функцію «Зміна пароля». Спроектуйте необхідну фікстуру. Які поля має мати об'єкт User у цій фікстурі, щоб тест був повноцінним?


5. 💡 Мислення як у розробника

Як відрізнити новачка від профі? По тому, як вони працюють з даними в тестах.

🚫 Типові помилки новачків:

  1. "Hardcoding" всього: Пишуть SQL-запити INSERT INTO users... прямо всередині тестової функції. Код стає величезним і нечитабельним.
  2. Глобальні дані: Використовують одну й ту ж базу даних, не очищуючи її. Через тиждень тестів там "смітник", і ніхто не знає, чому тести падають.
  3. Надлишковість: Створюють юзера з усіма полями (ім'я, адреса, улюблений колір, знак зодіаку), коли для тесту потрібен лише email.

🧠 Як думає досвідчений інженер:

  • "Keep it minimal": Якщо я тестую логін, мені потрібен юзер з логіном і паролем. Мені байдуже на його адресу. Моя фікстура буде мінімальною.
  • Фабрики (Factories) замість статики: Замість жорстких JSON-файлів профі часто використовують "Фабрики" — код, який генерує дані на льоту. Наприклад: UserFactory.create(role='admin'). Це гнучкіше.
  • Швидкість: Тестова база має бути швидкою. Часто її розгортають у RAM (пам'яті), а не на диску, щоб тести "літали".

6. 🧩 Підсумок

Отже, що ми сьогодні вивчили?

  1. Тестувати на продакшені — табу. Нам потрібна ізольована Тестова БД.
  2. Фікстури — це наші "декорації", попередньо підготовлені дані, які гарантують передбачуваність.
  3. Життєвий цикл тесту: Setup -> Test -> Teardown. Прибрати за собою так само важливо, як і підготуватися.

Тепер ви вмієте не просто писати код, а створювати безпечне середовище для перевірки його на міцність.

🔜 У наступній серії: Добре, ми розібралися з базою даних. А що, якщо наш код надсилає запити на зовнішній сервер (наприклад, API Нової Пошти або платіжну систему)? Ми ж не будемо справді платити гроші в тестах? Тут на сцену виходять Mocks (Моки) — мистецтво створення фальшивих сервісів. Не перемикайтесь!


Це був CS50. Дякую за увагу! 👋