Тестирование — важная часть разработки любых приложений, включая те, что строятся с использованием FastAPI. Это помогает убедиться, что приложение работает как ожидается, и упрощает поддержку кода в будущем. В этой статье мы рассмотрим основы тестирования FastAPI-приложений, включая тестирование маршрутов, зависимостей и работу с мокированием базы данных. Мы также посмотрим, как использовать pytest для написания тестов, а также как эффективно мокировать компоненты для изоляции тестов.

Основы тестирования с pytest

Для тестирования FastAPI-приложений мы будем использовать библиотеку pytest. Это популярная библиотека для тестирования Python, которая имеет удобный синтаксис и множество встроенных возможностей, таких как параллельное выполнение тестов, фикстуры и возможность использования моков.

FastAPI и pytest отлично интегрируются друг с другом. Для тестирования API-маршрутов можно использовать TestClient от FastAPI, который позволяет отправлять HTTP-запросы к вашему приложению и проверять ответы.

Установка зависимостей:

pip install pytest fastapi

Тестирование маршрутов и зависимостей

Пример теста для маршрута

Предположим, у нас есть простое приложение FastAPI с маршрутом для получения информации о пользователе:

from fastapi import FastAPI

app = FastAPI()

@app.get("/users/{user_id}")
async def get_user(user_id: int):
    return {"user_id": user_id, "name": "John Doe"}

Чтобы протестировать этот маршрут, создадим тест с использованием TestClient:

from fastapi.testclient import TestClient
from myapp import app  # Импортируем приложение FastAPI

client = TestClient(app)

def test_get_user():
    response = client.get("/users/1")
    assert response.status_code == 200
    assert response.json() == {"user_id": 1, "name": "John Doe"}

Пояснение:

  • TestClient(app) — создает клиента для взаимодействия с приложением FastAPI. Это позволяет отправлять запросы без запуска сервера.
  • client.get(«/users/1») — отправляем GET-запрос на маршрут /users/1.
  • assert response.status_code == 200 — проверяем, что статус код ответа равен 200, что означает успешное выполнение запроса.
  • assert response.json() == {«user_id»: 1, «name»: «John Doe»} — проверяем, что тело ответа соответствует ожидаемому JSON-объекту.

Использование TestClient

TestClient в FastAPI позволяет нам тестировать API так, как если бы мы взаимодействовали с реальным сервером, но без необходимости запускать его. Он поддерживает все HTTP-методы и позволяет тестировать ответы, заголовки, статус коды и другие параметры.

Пример теста для POST-запроса

from fastapi.testclient import TestClient
from myapp import app

client = TestClient(app)

def test_create_user():
    user_data = {"name": "Alice", "age": 30}
    response = client.post("/users/", json=user_data)
    
    assert response.status_code == 201
    assert response.json() == {"name": "Alice", "age": 30}

Мокирование БД и зависимостей

В реальных приложениях с FastAPI, скорее всего, будет использоваться база данных для хранения данных, например, PostgreSQL или MySQL. В тестах может возникнуть необходимость изолировать взаимодействие с базой данных, чтобы не работать с настоящими данными. Для этого используется мокирование (замена реальной базы данных на фиктивную, которая будет эмулировать нужное поведение).

Пример мокирования зависимости

Предположим, у нас есть сервис, который взаимодействует с базой данных для получения информации о пользователе:

from fastapi import Depends

class Database:
    def get_user(self, user_id: int):
        return {"user_id": user_id, "name": "John Doe"}

def get_database():
    return Database()

@app.get("/users/{user_id}")
async def get_user(user_id: int, db: Database = Depends(get_database)):
    user = db.get_user(user_id)
    return user

В тестах мы можем мокировать зависимость get_database, чтобы избежать обращения к реальной базе данных:

from unittest.mock import MagicMock
from fastapi.testclient import TestClient
from myapp import app, get_database

client = TestClient(app)

def test_get_user_with_mock():
    mock_db = MagicMock()
    mock_db.get_user.return_value = {"user_id": 1, "name": "Mocked User"}
    
    # Мокируем зависимость get_database
    app.dependency_overrides[get_database] = lambda: mock_db

    response = client.get("/users/1")
    assert response.status_code == 200
    assert response.json() == {"user_id": 1, "name": "Mocked User"}

Написание тестов для обработки ошибок

Важно тестировать не только успешные сценарии, но и случаи, когда происходят ошибки. Например, если пользователь запрашивает несуществующего пользователя, сервер должен вернуть ошибку 404.

Пример теста для ошибки

def test_get_user_not_found():
    response = client.get("/users/999")  # Пользователя с ID 999 не существует
    assert response.status_code == 404
    assert response.json() == {"detail": "User not found"}

Заключение

Тестирование FastAPI-приложений с использованием pytest и TestClient — это мощный и гибкий инструмент для создания качественного кода. Важно не только проверять успешные сценарии, но и тестировать обработку ошибок, мокировать зависимости и базы данных, чтобы изолировать тесты и не зависеть от внешних ресурсов. В этой статье мы рассмотрели основы тестирования маршрутов, работы с моками и тестирования ошибок. Теперь у вас есть все необходимое для того, чтобы уверенно писать тесты для вашего FastAPI-приложения.

Тестирование — это не просто хорошая практика, а неотъемлемая часть процесса разработки, которая помогает создать надежные и масштабируемые приложения.