Короткий ответ
Нужно явно моделировать multi-tenant изоляцию в тестах — создавать как минимум два разных тестовых тенанта в большинстве интеграционных тестов на чувствительных к изоляции путях и явно проверять, что данные/действия одного тенанта не видны и не влияют на другой, а не полагаться на то, что тестовые данные случайно не пересекаются из-за разных ID.
Как это работает подробнее
В зависимости от архитектуры multi-tenancy (общая БД с tenant_id в каждой таблице, отдельная схема на тенанта, или отдельная БД на тенанта) тест должен воспроизводить именно тот механизм разделения, который используется в production — если изоляция держится на фильтрации по tenant_id в каждом запросе, тест обязан проверить, что забытый фильтр в одном из запросов действительно приводит к падению теста, а не остаётся незамеченным.
Сравнение
- Компромисс. Полная изоляция с отдельной БД/схемой на тестовый тенант в каждом прогоне даёт самую высокую уверенность и ближе всего к production-архитектуре, но увеличивает время и сложность тестовой инфраструктуры, особенно при модели «отдельная БД на тенанта» в большом масштабе. Более лёгкая модель — общая тестовая БД с tenant_id и обязательными cross-tenant проверками в тестах — дешевле поддерживать, но зависит от дисциплины обязательно включать такие проверки в каждый новый тест. Критерий выбора: модель изоляции должна в тестах соответствовать реальной production-модели multi-tenancy, а объём обязательных cross-tenant тестов — зависеть от цены утечки данных между клиентами для конкретного продукта (например, выше для медицинских/финансовых данных).
Пример кода
def test_user_cannot_read_order_from_another_tenant(api_client, tenant_a, tenant_b):
order = create_order(tenant=tenant_a, total=100)
response = api_client.get(
f"/orders/{order.id}",
headers={"X-Tenant-Id": tenant_b.id}, # запрос от имени другого тенанта
)
assert response.status_code == 404 # данные тенанта A не должны быть видны тенанту BЧто использовать на практике
- Практика. Эффективный паттерн — обязательный тестовый кейс «утечка между тенантами» для каждого нового эндпоинта, читающего данные по идентификатору: создать сущность у тенанта A, попытаться прочитать её с токеном/контекстом тенанта B и явно проверить отказ (404/403), а не просто успешный ответ для тенанта A в изоляции. Для схем/БД на тенанта тестовая инфраструктура должна уметь поднимать и мигрировать несколько тестовых тенантов параллельно без значительного роста времени прогона.
Подводные камни
- Риск. Писать интеграционные тесты только с одним тестовым тенантом «по умолчанию», из-за чего баг отсутствия фильтрации по tenant_id в одном из запросов остаётся незамеченным, пока не проявится в production как утечка данных между клиентами. Использовать предсказуемые, одинаковые для всех тестов ID тенантов и сущностей, из-за чего тесты могут случайно «совпасть» по данным и скрыть реальную проблему изоляции.