Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое Scope в PyTest?
Scope (область видимости или жизненный цикл) в PyTest — это ключевая концепция, определяющая продолжительность жизни фикстур (fixtures) и степень их переиспользования между тестами. Понимание scope критически важно для написания эффективных, быстрых и изолированных тестов, так как оно напрямую влияет на управление ресурсами (создание/закрытие соединений, подготовку данных, очистку) и порядок выполнения.
Основные уровни Scope и их поведение
PyTest предоставляет пять уровней scope, которые указываются через параметр scope при дефиниции фикстуры (@pytest.fixture(scope="...")):
function(по умолчанию)
* Фикстура создается и разрушается для **каждого отдельного теста**.
* Идеально для **изоляции тестов**, чтобы состояние одного теста не влияло на другой.
* Пример: фикстура, которая генерирует уникальные данные для каждого теста.
```python
import pytest
@pytest.fixture(scope="function")
def fresh_user_data():
# Генерирует новые данные перед каждым тестом
return {"username": f"user_{uuid.uuid4()}", "email": f"test{uuid.uuid4()}@example.com"}
def test_user_creation(fresh_user_data):
# Здесь fresh_user_data уникален для этого теста
assert "username" in fresh_user_data
```
2. class
* Фикстура создается один раз для **каждого тестового класса** и разрушается после выполнения всех его методов.
* Полезно, когда несколько тестов в одном классе работают с одним общим ресурсом.
* Фикстура вызывается в методах класса через параметр или `request.cls`.
```python
import pytest
@pytest.fixture(scope="class")
def shared_db_connection():
conn = create_db_connection()
yield conn
conn.close()
class TestUserSuite:
def test_insert_user(self, shared_db_connection):
# Все методы класса используют один connection
shared_db_connection.execute("INSERT INTO users ...")
def test_select_user(self, shared_db_connection):
# Здесь connection тот же, что в test_insert_user
result = shared_db_connection.execute("SELECT ...")
```
3. module
* Фикстура создается один раз для **каждого файла модуля** (`.py`) и разрушается после выполнения всех тестов в этом модуле.
* Эффективна для дорогостоящих операций, которые можно разделить между многими тестами одного модуля (например, запуск приложения, чтение конфигурационного файла).
```python
import pytest
@pytest.fixture(scope="module")
def app_instance():
# Создание тяжелого объекта приложения один раз на модуль
app = MyApplication(config="module_config.json")
yield app
app.shutdown()
# Все тесты в этом файле используют один app_instance
def test_app_endpoint_1(app_instance):
response = app_instance.get("/api/v1/status")
def test_app_endpoint_2(app_instance):
response = app_instance.post("/api/v1/data")
```
4. package (или session для более широкого контекста)
* Фикстура создается один раз для **каждой директории (пакета)**, содержащей тесты, и разрушается после выполнения всех тестов в этом пакете.
* Менее распространен, но полезен для ресурсов, специфичных для группы модулей.
session(наиболее широкий)
* Фикстура создается **единственный раз** за всю сессию выполнения тестов (например, при запуске `pytest` для всей проектной директории) и разрушается после завершения **всех тестов**.
* Идеальный выбор для **глобальных, дорогостоящих ресурсов**, которые должны быть доступны всем тестам и не могут/не должны пересоздаваться (например, подключение к удаленной базе данных, запуск Docker контейнера, глобальная конфигурация).
```python
import pytest
@pytest.fixture(scope="session")
def global_docker_container():
# Запуск контейнера один раз для всей сессии тестов
container = docker_client.containers.run("my-test-db", detach=True)
yield container
container.stop()
# Тесты из разных модулей и пакетов используют один контейнер
def test_from_module_a(global_docker_container):
# Проверка связи с контейнером
pass
def test_from_module_b(global_docker_container):
# Проверка другой функциональности с тем же контейнером
pass
```
Автоматическое управление зависимостями (Automatic Dependency Management)
PyTest автоматически управляет порядком создания и разрушения фикстур на основе их scope и зависимостей. Фикстура с более широким scope (например, session) будет создана раньше и разрушена позже, чем фикстуры с более узким scope (например, function), которые зависят от нее. Это гарантирует корректную иерархию ресурсов.
import pytest
# Session fixture создается ПЕРВЫМ
@pytest.fixture(scope="session")
def heavy_session_resource():
resource = start_heavy_resource()
yield resource
resource.stop()
# Module fixture создается ПОСЛЕ session, но раньше function
@pytest.fixture(scope="module")
def module_config(heavy_session_resource): # Зависит от session fixture!
config = load_config(heavy_session_resource)
yield config
# Function fixture создается ПОСЛЕ module и разрушается перед ним
@pytest.fixture(scope="function")
def test_data(module_config): # Зависит от module fixture!
data = generate_data(module_config)
yield data
Практические рекомендации по выбору Scope
- Изоляция и безопасность: Если тесты могут изменять состояние ресурса и влиять друг на друга — используйте
functionилиclass. - Производительность: Если создание ресурса занимает много времени (подключение к БД, запуск сервера), и тесты его не изменяют — используйте
moduleилиsession. - Чистота тестов: Фикстуры с широким scope часто используют
yield(генераторы) для четкого разделения этапов setup и teardown. - Контекст зависимости: Фикстура может зависеть от другой фикстуры только если ее scope равен или шире. Фикстура
functionможет зависеть отmodule, но фикстураsessionне может зависеть отfunction.
Понимание и правильное применение scope позволяет строить гибкую, эффективную и надежную инфраструктуру тестов, балансируя между изоляцией, производительностью и управлением ресурсами.