Тестирование Flask-приложения

Тесты ловят поломки до пользователей. Flask даёт тестовый клиент, который шлёт запросы приложению напрямую, без реального сервера и браузера.
Тут окупается фабрика: для тестов создаёшь отдельный экземпляр приложения с тестовой базой и TESTING=True. Тестовый клиент имитирует запросы (GET, POST), а ты проверяешь статус ответа и его содержимое. Быстро, изолированно, повторяемо.

Ручная проверка «потыкать в браузере» не масштабируется: на сотом маршруте ты не вспомнишь, что проверять. Автотесты делают это за секунды и не забывают. Flask создаёт тестовый клиент методом app.test_client() — он шлёт запросы прямо в WSGI-приложение, минуя сеть.

import pytest
from blog import create_app
from blog.extensions import db

@pytest.fixture
def client():
    app = create_app({"TESTING": True,
                       "SQLALCHEMY_DATABASE_URI": "sqlite:///:memory:"})
    with app.app_context():
        db.create_all()
        yield app.test_client()

def test_index(client):
    resp = client.get("/")
    assert resp.status_code == 200

def test_create_post(client):
    resp = client.post("/posts/new",
                       data={"title": "Тест", "body": "..."},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert "Тест".encode() in resp.data

Фикстура client собирает приложение с тестовой конфигурацией: TESTING=True и база в памяти (:memory:) — она существует только во время теста и не трогает боевые данные. follow_redirects=True заставляет клиент пройти по PRG-редиректу до финальной страницы.

Автотесты — это страховка, которая позволяет менять код без страха что-то сломать незаметно. Здесь окончательно окупается фабрика: для тестов ты создаёшь отдельный экземпляр приложения с TESTING=True и базой в памяти (:memory:), полностью изолированный от боевых данных. Тестовый клиент работает без сети и браузера — он формирует WSGI-запрос и вызывает приложение напрямую, поэтому сотни тестов проходят за секунды. Хороший тест проверяет одну ситуацию, начинает с чистого состояния (свежая фикстура) и сверяет и статус-код, и содержимое ответа. И не забывай follow_redirects при тестировании POST-форм: из-за паттерна PRG без него ты получишь 302-редирект вместо финальной страницы и не сможешь проверить результат.

Как работает под капотом

Тестовый клиент не поднимает сервер. Он формирует WSGI-окружение запроса и вызывает приложение напрямую, получая объект ответа. Поэтому тесты быстрые: нет сети, нет браузера — только вызов функции.

  test_client().get("/")
       │ собрать фейковый запрос (WSGI environ)
       ▼ вызвать app(environ) напрямую (без сети)
  пройти маршрут → view → ответ
       ▼
  Response с .status_code и .data
       ▼ assert проверяет результат

Смоделируем тестовый клиент обычным Python поверх «приложения»-словаря маршрутов.

routes = {
    ("GET", "/"): lambda: (200, "Главная"),
    ("GET", "/about"): lambda: (200, "О нас"),
}

class TestClient:
    def request(self, method, path):
        view = routes.get((method, path))
        if not view:
            return (404, "Not Found")
        return view()

def test_index():
    c = TestClient()
    status, body = c.request("GET", "/")
    assert status == 200
    assert "Главная" in body
    return "test_index OK"

def test_missing():
    c = TestClient()
    status, _ = c.request("GET", "/none")
    assert status == 404
    return "test_missing OK"

print(test_index())
print(test_missing())

Запусти: клиент «шлёт» запросы и возвращает ответ, а assert проверяет статус и содержимое — ровно как настоящий test_client. Зелёные тесты = уверенность, что приложение работает.

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

  • Тестировать на боевой базе. Используй отдельную/in-memory базу через тестовую конфигурацию фабрики.
  • Забыть follow_redirects при POST. Из-за PRG ответ будет 302, а не финальные 200; проверка содержимого провалится.
  • Не изолировать тесты. Каждый тест должен начинать с чистого состояния (свежая фикстура).

Best practices

  • Конфигурируй тестовое приложение через аргумент фабрики (TESTING, in-memory БД).
  • Один тест — одна проверяемая ситуация; имена тестов описывают сценарий.
  • Проверяй и статус-код, и содержимое ответа, не что-то одно.

Что запомнить

  • test_client вызывает приложение напрямую через WSGI, без сети.
  • Фабрика даёт изолированный экземпляр с TESTING=True и in-memory базой.
  • Каждый тест — одна ситуация, с чистого состояния.
  • Проверяй и статус-код, и тело; для POST используй follow_redirects.

Итог: test_client шлёт запросы приложению напрямую, фабрика даёт изолированный экземпляр с тестовой базой, а assert проверяет статус и тело. Дальше — финальный урок: деплой в продакшен.

Проверьте себя
1. Почему для тестов используют app.test_client(), а не реальный сервер?
AТак красивее
BКлиент вызывает приложение напрямую через WSGI — без сети и браузера, тесты быстрые и изолированные
Ctest_client единственный способ запустить Flask
DРеальный сервер не поддерживает GET
2. Зачем в тесте POST-формы указывают follow_redirects=True?
AДля скорости
BЧтобы пройти PRG-редирект (302) до финальной страницы и проверить её содержимое
CЧтобы отключить CSRF
DЭто обязательный аргумент любого запроса