JavaRush /Курси /Модуль 4: FastAPI /Тестування CRUD-операцій у FastAPI і Django через SQLAlch...

Тестування CRUD-операцій у FastAPI і Django через SQLAlchemy

Модуль 4: FastAPI
Рівень 6 , Лекція 9
Відкрита

Признайтеся, багато хто вважає тестування нудною рутиною. Але насправді це наш надійний щит від катастроф! Уявіть, що ваш код — це океанський лайнер, а тести — команда уважних інженерів, які постійно перевіряють, чи не почав він десь протікати. CRUD-операції — у самому серці будь-якого застосунку, і збої в їхній роботі можуть обернутися справжньою катастрофою: від втрати важливих даних до повного краху бізнес-логіки.

Тестування CRUD-операцій стає не просто корисним, а необхідним з кількох причин:

  1. Перевірка коректності роботи API: тільки за допомогою тестів можна бути по-справжньому впевненим, що ваш код правильно виконує базові запити Create, Read, Update, Delete.
  2. Запобігання багам: помилки при роботі з базою даних особливо підступні — хороші тести працюють як страховка від найнеприємніших сюрпризів.
  3. Спокій при розширенні: коли ви додаєте нові фічі, працюючі тести дають упевненість, що ви не зламали вже існуючий функціонал.

Підходи до тестування

  1. Тестування на рівні бази даних.
    На цьому рівні ми перевіряємо, що SQLAlchemy виконує операції з базою даних коректно. Наприклад, що модель зберігається правильно, дані читаються так, як ми очікуємо, і жодні унікальності чи обмеження на рівні бази не порушуються.
  2. Тестування API.
    Якщо ваш застосунок надає REST API, то ми тестуємо кінцеві точки (endpoints) — перевіряємо, як вони обробляють запити і повертають відповіді. Це важливо для перевірки роботи застосунку "ззовні".
  3. Інтеграційне тестування.
    Таке тестування охоплює більші компоненти системи. Наприклад, можна перевірити, що виклик конкретного API у FastAPI дійсно створює запис у базі через SQLAlchemy.

Написання тестів для CRUD-операцій: практика

Давайте розберемо, як це все виглядає в коді! Для цього ми повторно використаємо наше застосунок на FastAPI, який був створений у попередніх лекціях.

Перш ніж почати тестування, треба підготувати тестове оточення. Ми будемо використовувати бібліотеку Pytest, а для тестування бази даних створимо спеціальну тестову базу.

Встановимо залежності для тестів:


pip install pytest pytest-asyncio

Якщо ви використовуєте PostgreSQL або будь-яку іншу СУБД, додатково встановіть драйвери, необхідні для підключення, наприклад:


pip install psycopg2

Приклад тестування CRUD-операцій у FastAPI

Для початку створимо найпростішу структуру застосунку. Нехай це буде API для керування користувачами.

Модель користувача


from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, nullable=False)
    email = Column(String, unique=True, nullable=False)

CRUD-операції

Створимо файл crud.py з основними функціями для взаємодії з базою.


from sqlalchemy.orm import Session
from .models import User

def create_user(db: Session, name: str, email: str):
    user = User(name=name, email=email)
    db.add(user)
    db.commit()
    db.refresh(user)
    return user

def get_user(db: Session, user_id: int):
    return db.query(User).filter(User.id == user_id).first()

def update_user(db: Session, user_id: int, name: str):
    user = db.query(User).filter(User.id == user_id).first()
    if user:
        user.name = name
        db.commit()
        db.refresh(user)
    return user

def delete_user(db: Session, user_id: int):
    user = db.query(User).filter(User.id == user_id).first()
    if user:
        db.delete(user)
        db.commit()
    return user

Тестування CRUD-операцій

Тепер перейдемо до тестів. Створимо файл test_crud.py.

Створимо тимчасову базу даних в пам'яті (SQLite) для забезпечення ізоляції тестів.


import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from ..models import Base

SQLALCHEMY_DATABASE_URL = "sqlite:///:memory:"

engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

@pytest.fixture(scope="function")
def db():
    Base.metadata.create_all(bind=engine)
    session = TestingSessionLocal()
    try:
        yield session
    finally:
        session.close()

Тести CREATE і READ. Тестуємо створення запису і читання цього ж запису.


from ..crud import create_user, get_user

def test_create_user(db):
    user = create_user(db, name="John Doe", email="john.doe@example.com")
    assert user.id is not None
    assert user.name == "John Doe"
    assert user.email == "john.doe@example.com"

def test_get_user(db):
    user = create_user(db, name="John Doe", email="john.doe@example.com")
    fetched_user = get_user(db, user.id)
    assert fetched_user == user

Тести UPDATE і DELETE. Оновимо ім'я користувача і видалимо його.


from ..crud import create_user, update_user, delete_user, get_user

def test_update_user(db):
    user = create_user(db, name="John Doe", email="john.doe@example.com")
    updated_user = update_user(db, user.id, name="Jane Doe")
    assert updated_user.name == "Jane Doe"

def test_delete_user(db):
    user = create_user(db, name="John Doe", email="john.doe@example.com")
    delete_user(db, user.id)
    assert get_user(db, user.id) is None

Приклад тестування через API FastAPI

Давайте тепер протестуємо API, яке використовує ці CRUD-функції.

Для тестування FastAPI-застосунків використовується стандартний TestClient з бібліотеки FastAPI.


from fastapi.testclient import TestClient
from ..main import app

client = TestClient(app)

def test_create_user_api():
    response = client.post("/users/", json={"name": "John Doe", "email": "john.doe@example.com"})
    assert response.status_code == 200
    assert response.json()["name"] == "John Doe"

Тестування в Django

Якщо замість FastAPI ви використовуєте Django, процес виглядатиме трохи інакше. Django надає вбудований TestCase для роботи з тестами. Ось приклад:


from django.test import TestCase
from .models import User

class UserCRUDTests(TestCase):
    def test_create_user(self):
        user = User.objects.create(name="John Doe", email="john.doe@example.com")
        self.assertIsNotNone(user.id)
        self.assertEqual(user.name, "John Doe")
        self.assertEqual(user.email, "john.doe@example.com")

    def test_update_user(self):
        user = User.objects.create(name="John Doe", email="john.doe@example.com")
        user.name = "Jane Doe"
        user.save()
        self.assertEqual(user.name, "Jane Doe")
    
    def test_delete_user(self):
        user = User.objects.create(name="John Doe", email="john.doe@example.com")
        user_id = user.id
        user.delete()
        self.assertFalse(User.objects.filter(id=user_id).exists())

Корисні поради

  • Фікстури для ізоляції: використовуйте фікстури Pytest або Django для створення тестових даних. Це допоможе уникнути небажаного забруднення тестового середовища.
  • Mock-тести: якщо ви тестуєте функції, які взаємодіють із зовнішніми API/сервісами, використовуйте unittest.mock.
  • Інтеграція в CI/CD: автоматизуйте запуск тестів у своєму CI/CD процесі, щоб одразу виявляти помилки.

pytest --cov=your_project/

Тепер ви можете бути впевнені, що ваші CRUD-операції працюють стабільно, незалежно від оточення та змін у коді!

3
Опитування
Взаємодія з базою даних через SQLAlchemy у FastAPI, рівень 6, лекція 9
Недоступний
Взаємодія з базою даних через SQLAlchemy у FastAPI
Взаємодія з базою даних через SQLAlchemy у FastAPI
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ