test-setup-async
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSetup Async Testing
异步测试环境搭建
Purpose
用途
Provide correct patterns for testing async functions in Python using pytest-asyncio, AsyncMock, and async fixtures. Ensures tests properly handle async context managers, side effects, and cleanup.
提供使用pytest-asyncio、AsyncMock和异步fixture在Python中测试异步函数的正确模式,确保测试能正确处理异步上下文管理器、副作用和资源清理。
Quick Start
快速开始
Most common use case:
python
undefined最常见使用场景:
python
undefinedTesting an async function with mocked dependencies
Testing an async function with mocked dependencies
import pytest
from unittest.mock import AsyncMock
@pytest.mark.asyncio
async def test_my_async_function():
mock_service = AsyncMock()
mock_service.fetch_data.return_value = {"result": "success"}
result = await my_async_function(mock_service)
assert result == {"result": "success"}
mock_service.fetch_data.assert_awaited_once()undefinedimport pytest
from unittest.mock import AsyncMock
@pytest.mark.asyncio
async def test_my_async_function():
mock_service = AsyncMock()
mock_service.fetch_data.return_value = {"result": "success"}
result = await my_async_function(mock_service)
assert result == {"result": "success"}
mock_service.fetch_data.assert_awaited_once()undefinedTable of Contents
目录
When to Use This Skill
何时使用该技能
Use this skill when:
- Testing async functions - Functions using and
async defawait - Creating async fixtures - Setup/teardown with async operations
- Mocking async services - Database connections, API clients, external services
- Handling async context managers - statements
async with - Testing async generators - patterns
async for - Debugging async test failures - "RuntimeWarning: coroutine was never awaited"
Trigger phrases:
- "Test async function"
- "Mock async method"
- "Create async fixture"
- "AsyncMock configuration"
- "pytest-asyncio setup"
在以下场景使用本技能:
- 测试异步函数 - 使用和
async def定义的函数await - 创建异步fixture - 包含异步操作的初始化/清理逻辑
- 模拟异步服务 - 数据库连接、API客户端、外部服务
- 处理异步上下文管理器 - 语句
async with - 测试异步生成器 - 模式
async for - 调试异步测试失败 - 出现"RuntimeWarning: coroutine was never awaited"警告时
触发场景:
- "测试异步函数"
- "模拟异步方法"
- "创建异步fixture"
- "AsyncMock配置"
- "pytest-asyncio环境搭建"
What This Skill Does
该技能能实现什么
This skill provides patterns for:
- Async test configuration - pytest-asyncio setup in pyproject.toml
- Async fixture creation - Using @pytest_asyncio.fixture with AsyncGenerator
- AsyncMock usage - Mocking async methods with return_value and side_effect
- Async context managers - Mocking statements (aenter, aexit)
async with - Async assertions - assert_awaited_once(), assert_awaited_once_with()
- Error handling - Testing exceptions in async code
See Instructions section below for detailed step-by-step patterns.
本技能提供以下场景的实现模式:
- 异步测试配置 - 在pyproject.toml中配置pytest-asyncio
- 异步fixture创建 - 使用@pytest_asyncio.fixture结合AsyncGenerator
- AsyncMock使用 - 为异步方法设置return_value和side_effect
- 异步上下文管理器 - 模拟语句的__aenter__和__aexit__方法
async with - 异步断言 - 使用assert_awaited_once()、assert_awaited_once_with()等断言方法
- 错误处理 - 测试异步代码中的异常
详细的分步模式请查看下方的操作步骤章节。
Instructions
操作步骤
Step 1: Install Dependencies
步骤1:安装依赖
bash
uv pip install pytest-asyncioAdd to :
pyproject.tomltoml
[tool.pytest.ini_options]
asyncio_mode = "auto" # Auto-detect async testsbash
uv pip install pytest-asyncio在中添加配置:
pyproject.tomltoml
[tool.pytest.ini_options]
asyncio_mode = "auto" # Auto-detect async testsStep 2: Choose Fixture Type
步骤2:选择Fixture类型
Async Fixture (for setup/teardown):
python
import pytest_asyncio
from collections.abc import AsyncGenerator
@pytest_asyncio.fixture
async def database_connection() -> AsyncGenerator[Connection, None]:
"""Create database connection with cleanup."""
conn = await create_connection()
yield conn
await conn.close()Sync Fixture (for mocks):
python
@pytest.fixture
def mock_service():
"""Create mock service (non-async fixture for mocks)."""
from unittest.mock import AsyncMock
mock = AsyncMock()
mock.fetch_data.return_value = {"data": "test"}
return mock异步Fixture(用于初始化/清理):
python
import pytest_asyncio
from collections.abc import AsyncGenerator
@pytest_asyncio.fixture
async def database_connection() -> AsyncGenerator[Connection, None]:
"""Create database connection with cleanup."""
conn = await create_connection()
yield conn
await conn.close()同步Fixture(用于模拟对象):
python
@pytest.fixture
def mock_service():
"""Create mock service (non-async fixture for mocks)."""
from unittest.mock import AsyncMock
mock = AsyncMock()
mock.fetch_data.return_value = {"data": "test"}
return mockStep 3: Mock Async Methods
步骤3:模拟异步方法
Simple return value:
python
mock_service = AsyncMock()
mock_service.process.return_value = "result"Side effect (exception):
python
mock_service.process.side_effect = Exception("Connection failed")Side effect (sequence):
python
mock_service.fetch.side_effect = [
{"batch": 1},
{"batch": 2},
{"batch": 3}
]Side effect (custom function):
python
async def custom_behavior(arg):
if arg == "fail":
raise ValueError("Invalid")
return f"processed_{arg}"
mock_service.process.side_effect = custom_behavior设置简单返回值:
python
mock_service = AsyncMock()
mock_service.process.return_value = "result"设置副作用(异常):
python
mock_service.process.side_effect = Exception("Connection failed")设置副作用(序列返回):
python
mock_service.fetch.side_effect = [
{"batch": 1},
{"batch": 2},
{"batch": 3}
]设置副作用(自定义函数):
python
async def custom_behavior(arg):
if arg == "fail":
raise ValueError("Invalid")
return f"processed_{arg}"
mock_service.process.side_effect = custom_behaviorStep 4: Test Async Context Managers
步骤4:测试异步上下文管理器
Mock async context manager:
python
from unittest.mock import AsyncMock
@pytest.fixture
def mock_driver():
"""Mock Neo4j driver with async context manager."""
driver = MagicMock() # Driver itself is sync
# Create async session mock
mock_session = AsyncMock()
mock_session.__aenter__ = AsyncMock(return_value=mock_session)
mock_session.__aexit__ = AsyncMock(return_value=None)
# Configure session behavior
mock_result = AsyncMock()
mock_result.data = AsyncMock(return_value=[{"node": "data"}])
mock_session.run = AsyncMock(return_value=mock_result)
# Driver returns session
driver.session = MagicMock(return_value=mock_session)
return driverTest usage:
python
@pytest.mark.asyncio
async def test_with_session(mock_driver):
async with mock_driver.session() as session:
result = await session.run("MATCH (n) RETURN n")
data = await result.data()
assert data == [{"node": "data"}]
mock_driver.session.assert_called_once()模拟异步上下文管理器:
python
from unittest.mock import AsyncMock
@pytest.fixture
def mock_driver():
"""Mock Neo4j driver with async context manager."""
driver = MagicMock() # Driver itself is sync
# Create async session mock
mock_session = AsyncMock()
mock_session.__aenter__ = AsyncMock(return_value=mock_session)
mock_session.__aexit__ = AsyncMock(return_value=None)
# Configure session behavior
mock_result = AsyncMock()
mock_result.data = AsyncMock(return_value=[{"node": "data"}])
mock_session.run = AsyncMock(return_value=mock_result)
# Driver returns session
driver.session = MagicMock(return_value=mock_session)
return driver测试使用示例:
python
@pytest.mark.asyncio
async def test_with_session(mock_driver):
async with mock_driver.session() as session:
result = await session.run("MATCH (n) RETURN n")
data = await result.data()
assert data == [{"node": "data"}]
mock_driver.session.assert_called_once()Step 5: Verify Async Calls
步骤5:验证异步调用
Assert awaited:
python
mock_service.fetch_data.assert_awaited_once()
mock_service.fetch_data.assert_awaited_once_with(arg="value")
mock_service.fetch_data.assert_awaited() # At least onceAssert call count:
python
assert mock_service.fetch_data.await_count == 3Assert not called:
python
mock_service.error_handler.assert_not_awaited()断言已被等待:
python
mock_service.fetch_data.assert_awaited_once()
mock_service.fetch_data.assert_awaited_once_with(arg="value")
mock_service.fetch_data.assert_awaited() # At least once断言调用次数:
python
assert mock_service.fetch_data.await_count == 3断言未被调用:
python
mock_service.error_handler.assert_not_awaited()Usage Examples
使用示例
Example 1: Async Fixture with Cleanup
示例1:带清理逻辑的异步Fixture
python
import pytest_asyncio
from collections.abc import AsyncGenerator
from neo4j import AsyncDriver, AsyncGraphDatabase
@pytest_asyncio.fixture(scope="function")
async def neo4j_driver() -> AsyncGenerator[AsyncDriver, None]:
"""Create Neo4j driver with cleanup."""
driver = AsyncGraphDatabase.driver(
"bolt://localhost:7687",
auth=("neo4j", "password")
)
# Setup: Clear test data
async with driver.session() as session:
await session.run("MATCH (n:TestNode) DETACH DELETE n")
yield driver
# Cleanup: Close driver
await driver.close()python
import pytest_asyncio
from collections.abc import AsyncGenerator
from neo4j import AsyncDriver, AsyncGraphDatabase
@pytest_asyncio.fixture(scope="function")
async def neo4j_driver() -> AsyncGenerator[AsyncDriver, None]:
"""Create Neo4j driver with cleanup."""
driver = AsyncGraphDatabase.driver(
"bolt://localhost:7687",
auth=("neo4j", "password")
)
# Setup: Clear test data
async with driver.session() as session:
await session.run("MATCH (n:TestNode) DETACH DELETE n")
yield driver
# Cleanup: Close driver
await driver.close()Example 2: AsyncMock with Side Effect Sequence
示例2:带序列副作用的AsyncMock
python
import pytest
from unittest.mock import AsyncMock
@pytest.mark.asyncio
async def test_batch_processing():
mock_api = AsyncMock()
# First call succeeds, second fails, third succeeds
mock_api.fetch_batch.side_effect = [
{"batch": 1, "items": [1, 2, 3]},
Exception("Rate limit exceeded"),
{"batch": 2, "items": [4, 5, 6]}
]
# Process first batch
result1 = await mock_api.fetch_batch()
assert result1["batch"] == 1
# Second call raises exception
with pytest.raises(Exception, match="Rate limit"):
await mock_api.fetch_batch()
# Third call succeeds
result3 = await mock_api.fetch_batch()
assert result3["batch"] == 2
assert mock_api.fetch_batch.await_count == 3python
import pytest
from unittest.mock import AsyncMock
@pytest.mark.asyncio
async def test_batch_processing():
mock_api = AsyncMock()
# First call succeeds, second fails, third succeeds
mock_api.fetch_batch.side_effect = [
{"batch": 1, "items": [1, 2, 3]},
Exception("Rate limit exceeded"),
{"batch": 2, "items": [4, 5, 6]}
]
# Process first batch
result1 = await mock_api.fetch_batch()
assert result1["batch"] == 1
# Second call raises exception
with pytest.raises(Exception, match="Rate limit"):
await mock_api.fetch_batch()
# Third call succeeds
result3 = await mock_api.fetch_batch()
assert result3["batch"] == 2
assert mock_api.fetch_batch.await_count == 3Example 3: Testing Service with Async Dependencies
示例3:测试带有异步依赖的服务
python
import pytest
import pytest_asyncio
from unittest.mock import AsyncMock
from project_watch_mcp.domain.values.service_result import ServiceResult
@pytest.fixture
def mock_embedding_service():
"""Create mock embedding service."""
service = AsyncMock()
service.get_embeddings.return_value = ServiceResult.success(
[[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]]
)
return service
@pytest.fixture
def mock_repository():
"""Create mock code repository."""
repo = AsyncMock()
repo.store_chunks.return_value = ServiceResult.success(True)
return repo
@pytest.mark.asyncio
async def test_chunking_service(mock_embedding_service, mock_repository):
"""Test chunking service with async dependencies."""
from project_watch_mcp.application.services.chunking_service import ChunkingService
# Create service with mocked dependencies
service = ChunkingService(
embedding_service=mock_embedding_service,
repository=mock_repository,
settings=mock_settings
)
# Execute async operation
result = await service.process_file("test.py")
# Verify async calls
mock_embedding_service.get_embeddings.assert_awaited_once()
mock_repository.store_chunks.assert_awaited_once()
assert result.is_success()python
import pytest
import pytest_asyncio
from unittest.mock import AsyncMock
from project_watch_mcp.domain.values.service_result import ServiceResult
@pytest.fixture
def mock_embedding_service():
"""Create mock embedding service."""
service = AsyncMock()
service.get_embeddings.return_value = ServiceResult.success(
[[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]]
)
return service
@pytest.fixture
def mock_repository():
"""Create mock code repository."""
repo = AsyncMock()
repo.store_chunks.return_value = ServiceResult.success(True)
return repo
@pytest.mark.asyncio
async def test_chunking_service(mock_embedding_service, mock_repository):
"""Test chunking service with async dependencies."""
from project_watch_mcp.application.services.chunking_service import ChunkingService
# Create service with mocked dependencies
service = ChunkingService(
embedding_service=mock_embedding_service,
repository=mock_repository,
settings=mock_settings
)
# Execute async operation
result = await service.process_file("test.py")
# Verify async calls
mock_embedding_service.get_embeddings.assert_awaited_once()
mock_repository.store_chunks.assert_awaited_once()
assert result.is_success()Example 4: Error Handling in Async Tests
示例4:异步测试中的错误处理
python
import pytest
from unittest.mock import AsyncMock
@pytest.mark.asyncio
async def test_error_handling():
"""Test service handles async errors correctly."""
mock_db = AsyncMock()
mock_db.execute_query.side_effect = Exception("Connection lost")
service = MyService(database=mock_db)
result = await service.fetch_data()
# Verify ServiceResult.failure() was returned
assert not result.is_success()
assert "Connection lost" in result.error
# Verify retry logic was triggered
assert mock_db.execute_query.await_count == 3 # 3 retriespython
import pytest
from unittest.mock import AsyncMock
@pytest.mark.asyncio
async def test_error_handling():
"""Test service handles async errors correctly."""
mock_db = AsyncMock()
mock_db.execute_query.side_effect = Exception("Connection lost")
service = MyService(database=mock_db)
result = await service.fetch_data()
# Verify ServiceResult.failure() was returned
assert not result.is_success()
assert "Connection lost" in result.error
# Verify retry logic was triggered
assert mock_db.execute_query.await_count == 3 # 3 retriesExample 5: Async Generator Fixture (Resource Pool)
示例5:异步生成器Fixture(资源池)
python
import pytest_asyncio
from collections.abc import AsyncGenerator
@pytest_asyncio.fixture
async def resource_pool() -> AsyncGenerator[ResourcePool, None]:
"""Create resource pool with async initialization and cleanup."""
pool = ResourcePool()
# Async initialization
await pool.initialize()
await pool.create_connections(count=5)
yield pool
# Async cleanup
await pool.drain()
await pool.shutdown()
@pytest.mark.asyncio
async def test_resource_acquisition(resource_pool):
"""Test acquiring resource from pool."""
async with resource_pool.acquire() as resource:
result = await resource.execute("SELECT 1")
assert result is not Nonepython
import pytest_asyncio
from collections.abc import AsyncGenerator
@pytest_asyncio.fixture
async def resource_pool() -> AsyncGenerator[ResourcePool, None]:
"""Create resource pool with async initialization and cleanup."""
pool = ResourcePool()
# Async initialization
await pool.initialize()
await pool.create_connections(count=5)
yield pool
# Async cleanup
await pool.drain()
await pool.shutdown()
@pytest.mark.asyncio
async def test_resource_acquisition(resource_pool):
"""Test acquiring resource from pool."""
async with resource_pool.acquire() as resource:
result = await resource.execute("SELECT 1")
assert result is not NoneExpected Outcomes
预期结果
Successful Async Test Setup
成功搭建异步测试环境
✅ Async Test Configured
Configuration:
✅ pytest-asyncio installed
✅ pyproject.toml configured (asyncio_mode = "auto")
✅ AsyncMock imported from unittest.mock
Test Results:
✅ All async tests discovered (@pytest.mark.asyncio)
✅ Async fixtures working (setup/teardown with yield)
✅ AsyncMock assertions passing (assert_awaited_once)
✅ No "coroutine was never awaited" warnings
Example test output:
tests/unit/test_service.py::test_async_function PASSED
tests/unit/test_service.py::test_with_async_fixture PASSED✅ Async Test Configured
Configuration:
✅ pytest-asyncio installed
✅ pyproject.toml configured (asyncio_mode = "auto")
✅ AsyncMock imported from unittest.mock
Test Results:
✅ All async tests discovered (@pytest.mark.asyncio)
✅ Async fixtures working (setup/teardown with yield)
✅ AsyncMock assertions passing (assert_awaited_once)
✅ No "coroutine was never awaited" warnings
Example test output:
tests/unit/test_service.py::test_async_function PASSED
tests/unit/test_service.py::test_with_async_fixture PASSEDCommon Failure - Mock Configuration Error
常见失败 - 模拟配置错误
❌ Test Failed: RuntimeWarning
Error: RuntimeWarning: coroutine 'AsyncMock' was never awaited
File "tests/unit/test_service.py", line 42
Root Cause:
- Used MagicMock instead of AsyncMock for async method
- Mock configured incorrectly
Fix:
# ❌ WRONG
mock_service.fetch = MagicMock(return_value="result")
# ✅ CORRECT
from unittest.mock import AsyncMock
mock_service.fetch = AsyncMock(return_value="result")❌ Test Failed: RuntimeWarning
Error: RuntimeWarning: coroutine 'AsyncMock' was never awaited
File "tests/unit/test_service.py", line 42
Root Cause:
- Used MagicMock instead of AsyncMock for async method
- Mock configured incorrectly
Fix:
# ❌ WRONG
mock_service.fetch = MagicMock(return_value="result")
# ✅ CORRECT
from unittest.mock import AsyncMock
mock_service.fetch = AsyncMock(return_value="result")Requirements
依赖要求
Dependencies:
- - Async test support
pytest-asyncio>=0.21.0 - - Test framework
pytest>=7.0.0 - Python 3.11+ with async/await support
Configuration:
toml
undefined依赖包:
- - 异步测试支持
pytest-asyncio>=0.21.0 - - 测试框架
pytest>=7.0.0 - Python 3.11+ 支持async/await
配置要求:
toml
undefinedpyproject.toml
pyproject.toml
[tool.pytest.ini_options]
asyncio_mode = "auto"
markers = [
"asyncio: mark test as async",
]
**Knowledge:**
- Python async/await syntax
- pytest fixture patterns
- Mock object configuration
- AsyncMock vs MagicMock differences
**Optional:**
- FastAPI or async web framework experience
- Database async drivers (asyncpg, motor, neo4j async driver)
---[tool.pytest.ini_options]
asyncio_mode = "auto"
markers = [
"asyncio: mark test as async",
]
**知识储备:**
- Python async/await语法
- pytest fixture模式
- 模拟对象配置
- AsyncMock与MagicMock的区别
**可选经验:**
- FastAPI或异步Web框架使用经验
- 数据库异步驱动(asyncpg, motor, neo4j async driver)使用经验
---Common Patterns
常见模式
Pattern 1: Mock with Both Sync and Async Methods
模式1:同时包含同步和异步方法的模拟
python
from unittest.mock import AsyncMock, MagicMock
mock = MagicMock()
mock.sync_method = MagicMock(return_value="sync_result")
mock.async_method = AsyncMock(return_value="async_result")python
from unittest.mock import AsyncMock, MagicMock
mock = MagicMock()
mock.sync_method = MagicMock(return_value="sync_result")
mock.async_method = AsyncMock(return_value="async_result")Usage
Usage
result1 = mock.sync_method() # Sync call
result2 = await mock.async_method() # Async call
undefinedresult1 = mock.sync_method() # Sync call
result2 = await mock.async_method() # Async call
undefinedPattern 2: Async Fixture Scope
模式2:异步Fixture作用域
python
undefinedpython
undefinedFunction scope (default) - new instance per test
Function scope (default) - new instance per test
@pytest_asyncio.fixture(scope="function")
async def function_scoped_resource():
resource = await create_resource()
yield resource
await resource.cleanup()
@pytest_asyncio.fixture(scope="function")
async def function_scoped_resource():
resource = await create_resource()
yield resource
await resource.cleanup()
Session scope - shared across all tests
Session scope - shared across all tests
@pytest_asyncio.fixture(scope="session")
async def session_scoped_resource():
resource = await create_expensive_resource()
yield resource
await resource.cleanup()
undefined@pytest_asyncio.fixture(scope="session")
async def session_scoped_resource():
resource = await create_expensive_resource()
yield resource
await resource.cleanup()
undefinedPattern 3: Parameterized Async Tests
模式3:参数化异步测试
python
@pytest.mark.asyncio
@pytest.mark.parametrize("input_value,expected", [
("test1", "result1"),
("test2", "result2"),
("test3", "result3"),
])
async def test_with_parameters(input_value, expected):
result = await async_function(input_value)
assert result == expectedpython
@pytest.mark.asyncio
@pytest.mark.parametrize("input_value,expected", [
("test1", "result1"),
("test2", "result2"),
("test3", "result3"),
])
async def test_with_parameters(input_value, expected):
result = await async_function(input_value)
assert result == expectedPattern 4: Testing Concurrent Operations
模式4:测试并发操作
python
import asyncio
import pytest
@pytest.mark.asyncio
async def test_concurrent_execution():
"""Test multiple async operations running concurrently."""
mock_service = AsyncMock()
mock_service.process.side_effect = [
asyncio.sleep(0.1, result="result1"),
asyncio.sleep(0.1, result="result2"),
asyncio.sleep(0.1, result="result3"),
]
# Execute concurrently
results = await asyncio.gather(
mock_service.process("item1"),
mock_service.process("item2"),
mock_service.process("item3"),
)
assert len(results) == 3
assert mock_service.process.await_count == 3python
import asyncio
import pytest
@pytest.mark.asyncio
async def test_concurrent_execution():
"""Test multiple async operations running concurrently."""
mock_service = AsyncMock()
mock_service.process.side_effect = [
asyncio.sleep(0.1, result="result1"),
asyncio.sleep(0.1, result="result2"),
asyncio.sleep(0.1, result="result3"),
]
# Execute concurrently
results = await asyncio.gather(
mock_service.process("item1"),
mock_service.process("item2"),
mock_service.process("item3"),
)
assert len(results) == 3
assert mock_service.process.await_count == 3Red Flags to Avoid
需要避免的常见错误
Mock Configuration Mistakes
模拟配置错误
- Using MagicMock for async methods - Always use AsyncMock for methods
async def - Missing return_value - AsyncMock() without return_value returns another AsyncMock
- Forgetting await - Calling async mock without causes warnings
await - Wrong assertion method - Use not
assert_awaited_once()for asyncassert_called_once() - Sync fixture for async resource - Use for async setup/teardown
@pytest_asyncio.fixture
- 使用MagicMock模拟异步方法 - 始终为方法使用AsyncMock
async def - 未设置return_value - 未设置return_value的AsyncMock会返回另一个AsyncMock
- 忘记使用await - 调用异步模拟方法时不使用会导致警告
await - 使用错误的断言方法 - 异步方法需使用而非
assert_awaited_once()assert_called_once() - 使用同步Fixture处理异步资源 - 异步初始化/清理需使用
@pytest_asyncio.fixture
Test Design Mistakes
测试设计错误
- No @pytest.mark.asyncio decorator - Async tests won't run correctly
- Mixing sync and async fixtures incorrectly - Driver is sync, session is async
- Not configuring asyncio_mode - Tests may fail inconsistently
- Using run_in_executor for simple async - Unnecessary complexity
- 缺少@pytest.mark.asyncio装饰器 - 异步测试无法正确运行
- 错误地混合同步和异步Fixture - 驱动是同步的,会话是异步的
- 未配置asyncio_mode - 测试可能会出现不一致的失败
- 为简单异步操作使用run_in_executor - 增加不必要的复杂度
Cleanup Mistakes
清理逻辑错误
- No cleanup in async fixtures - Resources leak (connections, files)
- Missing yield in fixture - Cleanup code never runs
- Exception in setup prevents cleanup - Use try/finally or pytest handles it
- 异步Fixture中无清理逻辑 - 会导致资源泄漏(连接、文件等)
- Fixture中缺少yield - 清理代码永远不会执行
- 初始化时的异常导致清理逻辑不执行 - 使用try/finally或依赖pytest的自动处理
Testing Mistakes
测试错误
- Not testing async context managers - Missing aenter/aexit mocks
- No side_effect for sequences - Can't test retry logic or multi-call scenarios
- Ignoring await_count - Not verifying async method called correct number of times
- 未测试异步上下文管理器 - 缺少对__aenter__/__aexit__的模拟
- 未设置序列型side_effect - 无法测试重试逻辑或多调用场景
- 忽略await_count - 未验证异步方法的调用次数是否正确
Troubleshooting
问题排查
Issue: "RuntimeWarning: coroutine was never awaited"
问题:"RuntimeWarning: coroutine was never awaited"
Cause: Mock returned coroutine instead of value
Fix: Use instead of for async methods
AsyncMockMagicMock原因: 模拟对象返回了协程而非具体值
解决: 为异步方法使用而非
AsyncMockMagicMockIssue: "TypeError: object AsyncMock can't be used in 'await' expression"
问题:"TypeError: object AsyncMock can't be used in 'await' expression"
Cause: Used for driver/client, should use
Fix: Outer object is sync, only context manager methods are async
AsyncMock()MagicMock()原因: 为驱动/客户端使用了,应使用
解决: 外层对象是同步的,仅上下文管理器方法是异步的
AsyncMock()MagicMock()Issue: "assert_awaited_once() raises AttributeError"
问题:"assert_awaited_once() raises AttributeError"
Cause: Used instead of
Fix: Async methods must use
MagicMockAsyncMockAsyncMock原因: 使用了而非
解决: 异步方法必须使用
MagicMockAsyncMockAsyncMockIssue: Fixture cleanup not running
问题:Fixture清理逻辑未执行
Cause: Exception during test prevents cleanup
Fix: Use in fixture or handles it
try/finally@pytest_asyncio.fixture原因: 测试过程中的异常导致清理逻辑无法执行
解决: 在Fixture中使用try/finally,或依赖@pytest_asyncio.fixture的自动处理
Integration Points
集成点
With Other Skills
与其他技能集成
setup-async-testing integrates with:
- implement-feature-complete - Provides async test patterns for Stage 2 (TDD)
- debug-test-failures - Works with async test debugging
- setup-pytest-fixtures - Combines sync and async fixture patterns
- implement-async-context-manager - Testing async context managers
setup-async-testing可与以下技能集成:
- implement-feature-complete - 为Stage 2(TDD)提供异步测试模式
- debug-test-failures - 配合进行异步测试调试
- setup-pytest-fixtures - 结合同步和异步Fixture模式
- implement-async-context-manager - 测试异步上下文管理器
With Agent Workflows
与Agent工作流集成
Agents should use this skill:
- @unit-tester - When testing async functions
- @integration-tester - For async database/API tests
- @implementer - During TDD workflow with async code
以下Agent应使用本技能:
- @unit-tester - 测试异步函数时
- @integration-tester - 进行异步数据库/API测试时
- @implementer - 在TDD工作流中处理异步代码时
With Testing Tools
与测试工具集成
Compatible with:
- pytest-asyncio (required)
- AsyncMock from unittest.mock
- pytest fixtures and markers
- Neo4j async driver testing
- FastAPI async endpoint testing
兼容工具:
- pytest-asyncio(必需)
- unittest.mock.AsyncMock
- pytest fixtures和markers
- Neo4j异步驱动测试
- FastAPI异步端点测试
Expected Benefits
预期收益
| Metric | Without Skill | With Skill | Improvement |
|---|---|---|---|
| Test Setup Time | 30 min (research) | 5 min (template) | 83% faster |
| Mock Configuration Errors | 40% (wrong mock type) | 5% (correct patterns) | 88% reduction |
| "Coroutine never awaited" Warnings | 20% of tests | 0% (proper AsyncMock) | 100% elimination |
| Async Fixture Cleanup Issues | 30% (leaks) | 0% (proper yield) | 100% fix |
| Test Reliability | 70% (flaky async) | 98% (stable) | 40% improvement |
| Knowledge Transfer Time | 2 hours (learning) | 15 min (examples) | 88% faster |
| 指标 | 未使用本技能 | 使用本技能 | 提升效果 |
|---|---|---|---|
| 测试环境搭建时间 | 30分钟(需调研) | 5分钟(使用模板) | 提升83%效率 |
| 模拟配置错误率 | 40%(错误的模拟类型) | 5%(使用正确模式) | 降低88%错误率 |
| "协程未被等待"警告 | 20%的测试出现 | 0%(正确使用AsyncMock) | 完全消除警告 |
| 异步Fixture资源泄漏 | 30%(资源未释放) | 0%(正确使用yield) | 完全解决泄漏问题 |
| 测试稳定性 | 70%(异步测试不稳定) | 98%(稳定运行) | 提升40%稳定性 |
| 知识传递时间 | 2小时(学习成本) | 15分钟(参考示例) | 提升88%效率 |
Success Metrics
成功指标
| Metric | Target | Measurement |
|---|---|---|
| Zero Async Warnings | 100% | pytest output |
| Mock Configuration Accuracy | 100% | AsyncMock validation |
| Fixture Cleanup Success | 100% | Resource leak detection |
| Test Reliability | 98%+ pass rate | CI/CD metrics |
| Pattern Adoption | 90% of async tests | Code review |
| 指标 | 目标值 | 测量方式 |
|---|---|---|
| 无异步警告 | 100% | pytest输出结果 |
| 模拟配置准确率 | 100% | AsyncMock验证 |
| Fixture清理成功率 | 100% | 资源泄漏检测 |
| 测试通过率 | 98%+ | CI/CD指标 |
| 模式使用率 | 90%的异步测试使用 | 代码评审 |
Validation Process
验证流程
Step 1: Dependency Validation
步骤1:依赖验证
bash
undefinedbash
undefinedEnsure pytest-asyncio installed
Ensure pytest-asyncio installed
uv pip list | grep pytest-asyncio
uv pip list | grep pytest-asyncio
Verify pyproject.toml configuration
Verify pyproject.toml configuration
cat pyproject.toml | grep asyncio_mode
undefinedcat pyproject.toml | grep asyncio_mode
undefinedStep 2: Test Configuration Validation
步骤2:测试配置验证
python
undefinedpython
undefinedCheck test file has correct decorator
Check test file has correct decorator
@pytest.mark.asyncio
async def test_name():
...
undefined@pytest.mark.asyncio
async def test_name():
...
undefinedStep 3: Mock Configuration Validation
步骤3:模拟配置验证
python
undefinedpython
undefinedVerify AsyncMock used for async methods
Verify AsyncMock used for async methods
from unittest.mock import AsyncMock
mock_service.async_method = AsyncMock(return_value=...)
undefinedfrom unittest.mock import AsyncMock
mock_service.async_method = AsyncMock(return_value=...)
undefinedStep 4: Fixture Validation
步骤4:Fixture验证
python
undefinedpython
undefinedAsync fixtures use AsyncGenerator
Async fixtures use AsyncGenerator
from collections.abc import AsyncGenerator
@pytest_asyncio.fixture
async def resource() -> AsyncGenerator[Resource, None]:
resource = await create()
yield resource
await resource.cleanup()
undefinedfrom collections.abc import AsyncGenerator
@pytest_asyncio.fixture
async def resource() -> AsyncGenerator[Resource, None]:
resource = await create()
yield resource
await resource.cleanup()
undefinedStep 5: Execution Validation
步骤5:执行验证
bash
undefinedbash
undefinedRun tests with verbose output
Run tests with verbose output
uv run pytest tests/test_async.py -v
uv run pytest tests/test_async.py -v
Verify no warnings
Verify no warnings
✓ No "RuntimeWarning: coroutine was never awaited"
✓ No "RuntimeWarning: coroutine was never awaited"
✓ All async assertions pass
✓ All async assertions pass
undefinedundefinedAutomation Scripts
自动化脚本
The directory contains production-ready automation utilities:
scripts/-
validate_async_tests.py - Validate async test patterns, detect anti-patternsbash
python scripts/validate_async_tests.py tests/unit/ --severity warning -
convert_to_async.py - Convert sync tests to async automaticallybash
python scripts/convert_to_async.py test_file.py --dry-run -
generate_async_fixture.py - Generate fixture boilerplatebash
python scripts/generate_async_fixture.py neo4j_driver database
📖 See references/SCRIPTS-REFERENCE.md for complete documentation and examples.
scripts/-
validate_async_tests.py - 验证异步测试模式,检测反模式bash
python scripts/validate_async_tests.py tests/unit/ --severity warning -
convert_to_async.py - 自动将同步测试转换为异步测试bash
python scripts/convert_to_async.py test_file.py --dry-run -
generate_async_fixture.py - 生成Fixture模板代码bash
python scripts/generate_async_fixture.py neo4j_driver database
📖 完整文档和示例请查看references/SCRIPTS-REFERENCE.md
Python Scripts
Python脚本列表
- convert_to_async.py - Convert synchronous tests to async test patterns
- generate_async_fixture.py - Generate async fixture boilerplate code
- validate_async_tests.py - Validate async test patterns and find anti-patterns
- convert_to_async.py - 将同步测试转换为异步测试模式
- generate_async_fixture.py - 生成异步Fixture模板代码
- validate_async_tests.py - 验证异步测试模式并查找反模式
See Also
相关链接
- pytest-asyncio documentation
- templates/async-test-template.py - Copy-paste template
- references/SCRIPTS-REFERENCE.md - Complete automation scripts documentation
- references/QUICK-REFERENCE.md - One-page quick reference
- references/IMPLEMENTATION-SUMMARY.md - Technical implementation details
- pytest-asyncio官方文档
- templates/async-test-template.py - 可直接复制使用的模板
- references/SCRIPTS-REFERENCE.md - 完整自动化脚本文档
- references/QUICK-REFERENCE.md - 单页速查手册
- references/IMPLEMENTATION-SUMMARY.md - 技术实现细节