MBE 测试指南
本文档介绍 MBE (Mises Behavior Engine) 的测试框架、测试编写和运行方法。
目录
测试框架
MBE 使用以下测试工具:
- pytest: 测试框架
- pytest-asyncio: 异步测试支持
- pytest-cov: 代码覆盖率
- pytest-mock: Mock 工具
- httpx: HTTP 测试客户端
- faker: 生成测试数据
安装测试依赖
pip install -r tests/requirements.txt
测试分类
1. 单元测试(Unit Tests)
测试单个函数或类,不依赖外部服务。
标记: @pytest.mark.unit
位置: tests/unit/
示例:
@pytest.mark.unit
def test_sanitize_input():
result = sanitize_input("<script>alert('xss')</script>")
assert "<script>" not in result
2. 集成测试(Integration Tests)
测试多个组件的交互,可能依赖数据库、Redis 等。
标记: @pytest.mark.integration
位置: tests/integration/
示例:
@pytest.mark.integration
@pytest.mark.db
async def test_database_connection(db_session):
result = await db_session.execute(text("SELECT 1"))
assert result.scalar() == 1
3. E2E 测试(End-to-End Tests)
测试完整的用户流程。
标记: @pytest.mark.e2e
位置: tests/e2e/(待实现)
运行测试
运行所有测试
pytest
运行特定测试类型
# 只运行单元测试
pytest -m unit
# 只运行集成测试
pytest -m integration
# 只运行需要数据库的测试
pytest -m db
# 只运行需要 Redis 的测试
pytest -m redis
运行特定测试文件
pytest tests/unit/test_utils.py
pytest tests/integration/test_api.py
运行特定测试函数
pytest tests/unit/test_utils.py::TestSecurity::test_validate_email
详细输出
pytest -v # 详细模式
pytest -vv # 更详细
pytest -s # 显示 print 输出
并行运行(加速)
pip install pytest-xdist
pytest -n auto # 自动使用所有 CPU 核心
pytest -n 4 # 使用 4 个进程
编写测试
测试文件命名
- 文件名:
test_*.py或*_test.py - 类名:
Test* - 函数名:
test_*
使用 Fixtures
Fixtures 在 tests/conftest.py 中定义。
1. HTTP 测试客户端
@pytest.mark.integration
async def test_api_endpoint(client: AsyncClient):
response = await client.get("/api/health")
assert response.status_code == 200
2. 数据库会话
@pytest.mark.integration
@pytest.mark.db
async def test_database_query(db_session):
result = await db_session.execute(text("SELECT 1"))
assert result.scalar() == 1
3. Redis 客户端
@pytest.mark.integration
@pytest.mark.redis
async def test_redis_operation(redis_client):
await redis_client.set("key", "value")
value = await redis_client.get("key")
assert value == "value"
4. 模拟配置
def test_with_custom_config(mock_settings):
mock_settings.enable_rate_limit = False
# 测试代码...
参数化测试
@pytest.mark.parametrize("email,expected", [
("test@example.com", True),
("invalid.email", False),
("", False),
])
def test_validate_email(email, expected):
assert validate_email(email) == expected
异步测试
@pytest.mark.asyncio
async def test_async_function():
result = await some_async_function()
assert result == expected
Mock 和 Patch
@pytest.mark.unit
def test_with_mock(mocker):
mock_func = mocker.patch("module.function")
mock_func.return_value = "mocked"
result = call_function_that_uses_mock()
assert result == "mocked"
mock_func.assert_called_once()
测试覆盖率
查看覆盖率报告
# 运行测试并生成覆盖率
pytest --cov
# 生成 HTML 报告
pytest --cov --cov-report=html
# 打开报告
open htmlcov/index.html # macOS
start htmlcov/index.html # Windows
xdg-open htmlcov/index.html # Linux
覆盖率配置
在 pyproject.toml 中配置:
[tool.pytest.ini_options]
addopts = [
"--cov=shared/src",
"--cov=private/core/src",
"--cov=private/platform/src",
"--cov-report=html",
"--cov-report=term-missing",
"--cov-fail-under=70", # 最低 70% 覆盖率
]
排除文件
创建 .coveragerc:
[run]
omit =
*/tests/*
*/migrations/*
*/__pycache__/*
*/venv/*
CI 集成
测试自动在 GitHub Actions 中运行。
CI 工作流
.github/workflows/ci.yml 包含:
- Validate - 验证 Monorepo 结构
- Lint - 代码风格检查
- Test Monorepo - 运行所有测试
- 单元测试
- 集成测试(带 PostgreSQL + Redis)
- 代码覆盖率上传到 Codecov
- Test Core - 测试私有核心模块
- Test Platform - 测试私有平台模块
- Test SDK - 测试公开 SDK
- Docker Build - 构建 Docker 镜像
本地模拟 CI
# 1. 验证
python tools/dev-workspace/check_boundaries.py --public-only
python tools/dev-workspace/validate_monorepo.py
# 2. Linting
ruff check shared/src/ private/core/src/ private/platform/src/
# 3. 测试(需要 Docker)
docker-compose -f docker-compose.dev.yml up -d postgres redis
pytest -m unit
pytest -m integration
docker-compose -f docker-compose.dev.yml down
测试最佳实践
1. 测试命名
# 好的命名
def test_user_login_with_valid_credentials():
...
def test_rate_limit_blocks_excessive_requests():
...
# 不好的命名
def test1():
...
def test_user():
...
2. AAA 模式
def test_function():
# Arrange(准备)
user = User(email="test@example.com")
# Act(执行)
result = user.validate_email()
# Assert(断言)
assert result is True
3. 一个测试一个断言(理想情况)
# 好
def test_user_email_is_validated():
assert validate_email("test@example.com") is True
def test_user_email_rejects_invalid():
assert validate_email("invalid") is False
# 避免
def test_user_email():
assert validate_email("test@example.com") is True
assert validate_email("invalid") is False
assert validate_email("") is False
# ... 太多断言
4. 使用 Fixture 而非全局变量
# 好
@pytest.fixture
def user():
return User(email="test@example.com")
def test_user_name(user):
assert user.email == "test@example.com"
# 避免
USER = User(email="test@example.com")
def test_user_name():
assert USER.email == "test@example.com"
5. 清理测试数据
@pytest.fixture
async def redis_client():
redis = await get_redis()
await redis.select(15) # 测试专用 DB
yield redis
# 清理
await redis.flushdb()
调试测试
使用 pdb
def test_something():
import pdb; pdb.set_trace()
result = function_to_test()
assert result == expected
使用 pytest --pdb
pytest --pdb # 失败时自动进入 pdb
pytest -x --pdb # 第一个失败时停止并进入 pdb
打印输出
pytest -s # 显示 print() 输出
只运行失败的测试
pytest --lf # 只运行上次失败的测试
pytest --ff # 先运行失败的,再运行其他
持续改进
提高测试覆盖率
# 查看未覆盖的行
pytest --cov --cov-report=term-missing
# 生成 HTML 报告查看详情
pytest --cov --cov-report=html
添加新测试
- 确定测试类型(单元/集成)
- 在对应目录创建测试文件
- 添加适当的标记(
@pytest.mark.unit) - 使用 Fixtures 简化测试
- 运行测试确保通过
- 提交代码
常见问题
Q: 测试失败但本地运行正常?
A: 检查环境变量和数据库连接。CI 使用测试数据库。
Q: 如何跳过某些测试?
A: 使用 @pytest.mark.skip:
@pytest.mark.skip(reason="Not implemented yet")
def test_future_feature():
...
Q: 如何标记慢测试?
A: 使用 @pytest.mark.slow:
@pytest.mark.slow
def test_expensive_operation():
...
# 跳过慢测试
pytest -m "not slow"
Q: 如何测试私有函数?
A: 通过公共 API 间接测试,或者重构代码使其可测试。
参考资源
总结
MBE 的测试框架提供:
✅ 单元测试 - 快速验证函数逻辑
✅ 集成测试 - 验证组件交互
✅ 代码覆盖率 - 确保测试充分
✅ CI 集成 - 自动运行测试
✅ Fixtures - 简化测试编写
✅ 异步支持 - 测试异步代码
目标:保持 70%+ 代码覆盖率,确保代码质量!