setup-pytest-fixtures
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSetup Pytest Fixtures
配置Pytest Fixture
Purpose
用途
Creates pytest fixtures following project-watch-mcp patterns: factory fixtures for customization, async fixtures for async code, centralized organization in tests/utils/, and proper conftest.py hierarchy.
按照project-watch-mcp项目规范创建pytest夹具:支持用于自定义的工厂式夹具、适配异步代码的异步夹具、集中存放在tests/utils/中的组织方式,以及合理的conftest.py层级结构。
Quick Start
快速开始
Create a basic fixture:
python
import pytest
@pytest.fixture
def mock_settings():
"""Create mock settings for testing."""
settings = MagicMock()
settings.project = MagicMock()
settings.project.project_name = "test_project"
return settingsCreate a factory fixture:
python
@pytest.fixture
def mock_settings_factory():
"""Factory for custom mock settings."""
def create_settings(**kwargs):
settings = MagicMock()
settings.project = MagicMock()
settings.project.project_name = kwargs.get("project_name", "test_project")
return settings
return create_settings创建基础夹具:
python
import pytest
@pytest.fixture
def mock_settings():
"""Create mock settings for testing."""
settings = MagicMock()
settings.project = MagicMock()
settings.project.project_name = "test_project"
return settings创建工厂式夹具:
python
@pytest.fixture
def mock_settings_factory():
"""Factory for custom mock settings."""
def create_settings(**kwargs):
settings = MagicMock()
settings.project = MagicMock()
settings.project.project_name = kwargs.get("project_name", "test_project")
return settings
return create_settingsInstructions
操作步骤
Step 1: Identify Fixture Type
步骤1:确定夹具类型
Determine which fixture pattern to use:
-
Basic Fixture: Simple, reusable test data
- Use for: Constant values, simple mocks
- Example:
mock_settings_minimal
-
Factory Fixture: Customizable with parameters
- Use for: Configurable test data, parameterized tests
- Example:
mock_settings_factory(**kwargs)
-
Async Fixture: For async operations
- Use for: Async setup/teardown, async resources
- Example:
@pytest_asyncio.fixture async def...
-
Scoped Fixture: Shared across multiple tests
- Use for: Expensive setup, session-wide resources
- Example:
@pytest.fixture(scope="session")
选择合适的夹具模式:
-
基础夹具:简单、可复用的测试数据
- 适用场景:常量值、简单模拟对象
- 示例:
mock_settings_minimal
-
工厂式夹具:支持参数自定义
- 适用场景:可配置的测试数据、参数化测试
- 示例:
mock_settings_factory(**kwargs)
-
异步夹具:适配异步操作
- 适用场景:异步初始化/清理、异步资源
- 示例:
@pytest_asyncio.fixture async def...
-
作用域夹具:跨多个测试用例共享
- 适用场景:开销较大的初始化、会话级资源
- 示例:
@pytest.fixture(scope="session")
Step 2: Choose Organization Strategy
步骤2:选择组织策略
Centralized Utilities (Recommended for reusable fixtures):
- Location:
tests/utils/ - Files: ,
mock_settings.py,mock_services.py,mock_drivers.pyenvironmental_helpers.py - Import in: to make available project-wide
tests/conftest.py
Local Conftest (For layer-specific fixtures):
- Location: ,
tests/unit/conftest.py,tests/integration/conftest.pytests/e2e/conftest.py - Use for: Fixtures specific to that test layer
Test File (For test-specific fixtures):
- Location: Same file as tests
- Use for: One-off fixtures not reused elsewhere
集中式工具库(推荐用于可复用夹具):
- 存放位置:
tests/utils/ - 文件示例:,
mock_settings.py,mock_services.py,mock_drivers.pyenvironmental_helpers.py - 导入方式:在中导入,实现项目全局可用
tests/conftest.py
本地Conftest(用于层级专属夹具):
- 存放位置:,
tests/unit/conftest.py,tests/integration/conftest.pytests/e2e/conftest.py - 适用场景:仅对应测试层级专属的夹具
测试文件内(用于测试专属夹具):
- 存放位置:与测试用例同一文件
- 适用场景:仅当前测试用例使用的一次性夹具
Step 3: Implement Fixture
步骤3:实现夹具
For Basic Fixtures:
python
import pytest
from unittest.mock import MagicMock
@pytest.fixture
def fixture_name():
"""Clear docstring explaining purpose."""
# Setup
obj = MagicMock()
obj.attribute = "value"
# Return (no yield for simple fixtures)
return objFor Factory Fixtures:
python
@pytest.fixture
def fixture_factory():
"""Factory for custom fixture."""
def create_fixture(**kwargs):
"""Create fixture with custom attributes.
Args:
**kwargs: Attributes to customize
Returns:
MagicMock: Configured fixture
"""
obj = MagicMock()
obj.attribute = kwargs.get("attribute", "default_value")
return obj
return create_fixtureFor Async Fixtures:
python
import pytest_asyncio
from collections.abc import AsyncGenerator
@pytest_asyncio.fixture(scope="function")
async def async_fixture() -> AsyncGenerator[Resource, None]:
"""Async fixture with setup and teardown."""
# Setup
resource = await create_resource()
yield resource
# Teardown
await resource.close()For Scoped Fixtures:
python
@pytest.fixture(scope="session")
def session_fixture():
"""Session-scoped fixture created once."""
# Expensive setup
resource = expensive_setup()
yield resource
# Cleanup after all tests
resource.cleanup()基础夹具实现:
python
import pytest
from unittest.mock import MagicMock
@pytest.fixture
def fixture_name():
"""Clear docstring explaining purpose."""
# Setup
obj = MagicMock()
obj.attribute = "value"
# Return (no yield for simple fixtures)
return obj工厂式夹具实现:
python
@pytest.fixture
def fixture_factory():
"""Factory for custom fixture."""
def create_fixture(**kwargs):
"""Create fixture with custom attributes.
Args:
**kwargs: Attributes to customize
Returns:
MagicMock: Configured fixture
"""
obj = MagicMock()
obj.attribute = kwargs.get("attribute", "default_value")
return obj
return create_fixture异步夹具实现:
python
import pytest_asyncio
from collections.abc import AsyncGenerator
@pytest_asyncio.fixture(scope="function")
async def async_fixture() -> AsyncGenerator[Resource, None]:
"""Async fixture with setup and teardown."""
# Setup
resource = await create_resource()
yield resource
# Teardown
await resource.close()作用域夹具实现:
python
@pytest.fixture(scope="session")
def session_fixture():
"""Session-scoped fixture created once."""
# Expensive setup
resource = expensive_setup()
yield resource
# Cleanup after all tests
resource.cleanup()Step 4: Add to Conftest Hierarchy
步骤4:添加到Conftest层级结构
In :
tests/utils/mock_*.pypython
"""Reusable mock fixtures for [component] components."""
from unittest.mock import MagicMock
import pytest
@pytest.fixture
def fixture_name():
"""Fixture docstring."""
return MagicMock()In :
tests/conftest.pypython
undefined在中:
tests/utils/mock_*.pypython
"""Reusable mock fixtures for [component] components."""
from unittest.mock import MagicMock
import pytest
@pytest.fixture
def fixture_name():
"""Fixture docstring."""
return MagicMock()在中:
tests/conftest.pypython
undefinedImport to make available project-wide
Import to make available project-wide
from tests.utils.mock_settings import (
fixture_name,
)
all = [
"fixture_name",
]
**In layer-specific conftest:**
```pythonfrom tests.utils.mock_settings import (
fixture_name,
)
all = [
"fixture_name",
]
**在层级专属Conftest中:**
```pythontests/unit/conftest.py
tests/unit/conftest.py
import pytest
@pytest.fixture
def unit_specific_fixture():
"""Fixture only for unit tests."""
return MagicMock()
undefinedimport pytest
@pytest.fixture
def unit_specific_fixture():
"""Fixture only for unit tests."""
return MagicMock()
undefinedStep 5: Use Fixtures in Tests
步骤5:在测试中使用夹具
Basic usage:
python
def test_something(fixture_name):
"""Test using fixture."""
assert fixture_name.attribute == "value"Factory usage:
python
def test_with_factory(fixture_factory):
"""Test using factory fixture."""
custom = fixture_factory(attribute="custom_value")
assert custom.attribute == "custom_value"Async usage:
python
@pytest.mark.asyncio
async def test_async(async_fixture):
"""Test using async fixture."""
result = await async_fixture.operation()
assert result == expectedMultiple fixtures:
python
def test_with_multiple(fixture_one, fixture_two, fixture_factory):
"""Test using multiple fixtures."""
custom = fixture_factory(value="test")
assert fixture_one.works_with(fixture_two, custom)基础用法:
python
def test_something(fixture_name):
"""Test using fixture."""
assert fixture_name.attribute == "value"工厂式用法:
python
def test_with_factory(fixture_factory):
"""Test using factory fixture."""
custom = fixture_factory(attribute="custom_value")
assert custom.attribute == "custom_value"异步用法:
python
@pytest.mark.asyncio
async def test_async(async_fixture):
"""Test using async fixture."""
result = await async_fixture.operation()
assert result == expected多夹具用法:
python
def test_with_multiple(fixture_one, fixture_two, fixture_factory):
"""Test using multiple fixtures."""
custom = fixture_factory(value="test")
assert fixture_one.works_with(fixture_two, custom)Examples
示例
Example 1: Basic Mock Fixture
示例1:基础模拟夹具
python
import pytest
from unittest.mock import MagicMock
@pytest.fixture
def mock_settings_minimal():
"""Create minimal mock settings for basic testing."""
settings = MagicMock()
settings.project = MagicMock()
settings.project.project_name = "test_project"
settings.neo4j = MagicMock()
settings.neo4j.database_name = "test_db"
return settingspython
import pytest
from unittest.mock import MagicMock
@pytest.fixture
def mock_settings_minimal():
"""Create minimal mock settings for basic testing."""
settings = MagicMock()
settings.project = MagicMock()
settings.project.project_name = "test_project"
settings.neo4j = MagicMock()
settings.neo4j.database_name = "test_db"
return settingsExample 2: Factory Fixture
示例2:工厂式夹具
python
@pytest.fixture
def mock_settings_factory():
"""Factory for custom mock settings."""
def create_settings(**kwargs):
settings = MagicMock()
settings.project = MagicMock()
settings.project.project_name = kwargs.get("project_name", "test_project")
settings.neo4j = MagicMock()
settings.neo4j.database_name = kwargs.get("database_name", "test_db")
settings.chunking = MagicMock()
settings.chunking.chunk_size = kwargs.get("chunk_size", 50)
return settings
return create_settingspython
@pytest.fixture
def mock_settings_factory():
"""Factory for custom mock settings."""
def create_settings(**kwargs):
settings = MagicMock()
settings.project = MagicMock()
settings.project.project_name = kwargs.get("project_name", "test_project")
settings.neo4j = MagicMock()
settings.neo4j.database_name = kwargs.get("database_name", "test_db")
settings.chunking = MagicMock()
settings.chunking.chunk_size = kwargs.get("chunk_size", 50)
return settings
return create_settingsExample 3: Async Fixture with Cleanup
示例3:带清理操作的异步夹具
python
import pytest_asyncio
from collections.abc import AsyncGenerator
from neo4j import AsyncGraphDatabase, AsyncDriver
@pytest_asyncio.fixture(scope="function")
async def real_neo4j_driver() -> AsyncGenerator[AsyncDriver, None]:
"""Create a real Neo4j driver with cleanup."""
driver = AsyncGraphDatabase.driver(
"bolt://localhost:7687",
auth=("neo4j", "password")
)
# Setup: Clear test data
async with driver.session(database="test_db") as session:
await session.run("MATCH (n {project_name: 'test_project'}) DETACH DELETE n")
yield driver
# Teardown: Close driver
await driver.close()Script Files:
- scripts/generate_fixture.py - Auto-generate pytest fixtures following project patterns
- scripts/analyze_fixture_usage.py - Analyze pytest fixture usage and find optimization opportunities
- scripts/organize_fixtures.py - Reorganize fixtures by layer and update imports
python
import pytest_asyncio
from collections.abc import AsyncGenerator
from neo4j import AsyncGraphDatabase, AsyncDriver
@pytest_asyncio.fixture(scope="function")
async def real_neo4j_driver() -> AsyncGenerator[AsyncDriver, None]:
"""Create a real Neo4j driver with cleanup."""
driver = AsyncGraphDatabase.driver(
"bolt://localhost:7687",
auth=("neo4j", "password")
)
# Setup: Clear test data
async with driver.session(database="test_db") as session:
await session.run("MATCH (n {project_name: 'test_project'}) DETACH DELETE n")
yield driver
# Teardown: Close driver
await driver.close()脚本文件:
- scripts/generate_fixture.py - 按照项目规范自动生成pytest夹具
- scripts/analyze_fixture_usage.py - 分析pytest夹具的使用情况并找出优化空间
- scripts/organize_fixtures.py - 按层级重新组织夹具并更新导入关系
Requirements
依赖要求
- pytest installed:
uv pip install pytest - pytest-asyncio for async fixtures:
uv pip install pytest-asyncio - Understanding of project structure:
- - Centralized reusable fixtures
tests/utils/ - - Project-wide fixture imports
tests/conftest.py - - Unit test specific fixtures
tests/unit/conftest.py - - Integration test specific fixtures
tests/integration/conftest.py - - E2E test specific fixtures
tests/e2e/conftest.py
- 已安装pytest:
uv pip install pytest - 异步夹具需要pytest-asyncio:
uv pip install pytest-asyncio - 了解项目结构:
- - 集中存放可复用夹具
tests/utils/ - - 项目全局夹具导入文件
tests/conftest.py - - 单元测试专属夹具
tests/unit/conftest.py - - 集成测试专属夹具
tests/integration/conftest.py - - 端到端测试专属夹具
tests/e2e/conftest.py
Fixture Best Practices
Fixture最佳实践
- Naming: Use descriptive names with prefix for mocks
mock_ - Docstrings: Always document what the fixture provides
- Scope: Default to scope, use
function/sessiononly when neededmodule - Cleanup: Use for fixtures that need teardown
yield - Factory Pattern: Return callable for customizable fixtures
- Type Hints: Add return type hints for better IDE support
- Reusability: Place reusable fixtures in , import in
tests/utils/conftest.py - Layer Isolation: Keep layer-specific fixtures in layer conftest files
- 命名规范:模拟对象夹具使用前缀,名称需具有描述性
mock_ - 文档字符串:始终说明夹具提供的功能
- 作用域选择:默认使用作用域,仅在必要时使用
function/session作用域module - 清理操作:需要清理的夹具使用而非
yieldreturn - 工厂模式:返回可调用对象以实现夹具自定义
- 类型提示:添加返回类型提示以提升IDE支持
- 复用性:可复用夹具放在中,在
tests/utils/中导入conftest.py - 层级隔离:层级专属夹具放在对应层级的conftest文件中
Common Patterns
常见模式
Settings Mock:
python
@pytest.fixture
def mock_settings():
"""Standard settings mock."""
settings = MagicMock()
settings.project = MagicMock()
settings.neo4j = MagicMock()
return settingsService Result Mock:
python
@pytest.fixture
def mock_service_result():
"""Factory for ServiceResult mocks."""
def create_result(success=True, data=None, error=None):
result = MagicMock()
result.is_success = success
result.data = data
result.error = error
return result
return create_resultAsync Service Mock:
python
@pytest.fixture
def mock_embedding_service():
"""Async embedding service mock."""
service = AsyncMock()
service.generate_embeddings = AsyncMock(
return_value=MagicMock(success=True, data=[[0.1] * 384])
)
return service设置模拟:
python
@pytest.fixture
def mock_settings():
"""Standard settings mock."""
settings = MagicMock()
settings.project = MagicMock()
settings.neo4j = MagicMock()
return settings服务结果模拟:
python
@pytest.fixture
def mock_service_result():
"""Factory for ServiceResult mocks."""
def create_result(success=True, data=None, error=None):
result = MagicMock()
result.is_success = success
result.data = data
result.error = error
return result
return create_result异步服务模拟:
python
@pytest.fixture
def mock_embedding_service():
"""Async embedding service mock."""
service = AsyncMock()
service.generate_embeddings = AsyncMock(
return_value=MagicMock(success=True, data=[[0.1] * 384])
)
return serviceTroubleshooting
故障排除
Fixture not found:
- Check it's imported in
tests/conftest.py - Verify file is in Python path
- Ensure includes fixture name
__all__
Async fixture errors:
- Install :
pytest-asynciouv pip install pytest-asyncio - Use not
@pytest_asyncio.fixture@pytest.fixture - Mark test with
@pytest.mark.asyncio
Scope issues:
- Session fixtures can't use function fixtures
- Use broader scope for dependent fixtures
- Consider fixture dependencies carefully
Cleanup not running:
- Use not
yieldreturn - Ensure async cleanup uses
await - Check for exceptions during test
找不到夹具:
- 检查是否已在中导入
tests/conftest.py - 验证文件是否在Python路径中
- 确保列表包含该夹具名称
__all__
异步夹具错误:
- 安装:
pytest-asynciouv pip install pytest-asyncio - 使用而非
@pytest_asyncio.fixture@pytest.fixture - 测试用例添加标记
@pytest.mark.asyncio
作用域问题:
- 会话级夹具无法使用函数级夹具
- 依赖夹具需使用更宽泛的作用域
- 仔细考虑夹具之间的依赖关系
清理操作未执行:
- 使用而非
yieldreturn - 异步清理操作需使用
await - 检查测试过程中是否出现异常
Automation Scripts
自动化脚本
NEW: Powerful automation utilities for fixture management:
新增功能: 强大的夹具管理自动化工具:
scripts/generate_fixture.py
scripts/generate_fixture.py
Auto-generate fixtures with proper patterns:
bash
python .claude/skills/setup-pytest-fixtures/scripts/generate_fixture.py \
--name mock_service --type factory --attributes host,port,timeout按照规范自动生成夹具:
bash
python .claude/skills/setup-pytest-fixtures/scripts/generate_fixture.py \
--name mock_service --type factory --attributes host,port,timeoutscripts/analyze_fixture_usage.py
scripts/analyze_fixture_usage.py
Analyze fixture usage and find optimization opportunities:
bash
python .claude/skills/setup-pytest-fixtures/scripts/analyze_fixture_usage.py --unused分析夹具使用情况并找出优化空间:
bash
python .claude/skills/setup-pytest-fixtures/scripts/analyze_fixture_usage.py --unusedscripts/organize_fixtures.py
scripts/organize_fixtures.py
Reorganize fixtures by layer and update imports:
bash
python .claude/skills/setup-pytest-fixtures/scripts/organize_fixtures.py --validate按层级重新组织夹具并更新导入关系:
bash
python .claude/skills/setup-pytest-fixtures/scripts/organize_fixtures.py --validateSee Also
相关链接
- templates/fixture-templates.md - Copy-paste templates
- - Settings fixture reference
tests/utils/mock_settings.py - - Service fixture reference
tests/utils/mock_services.py - - Driver fixture reference
tests/utils/mock_drivers.py - - Environment fixture reference
tests/utils/environmental_helpers.py
- templates/fixture-templates.md - 可直接复制使用的夹具模板
- - 设置夹具参考示例
tests/utils/mock_settings.py - - 服务夹具参考示例
tests/utils/mock_services.py - - 驱动夹具参考示例
tests/utils/mock_drivers.py - - 环境夹具参考示例
tests/utils/environmental_helpers.py