You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

6.2 KiB

测试目录结构重构

日期: 2026-01-27
类型: 重构
影响范围: 测试代码组织

背景

项目根目录下的 test_sms_service.pytest_wechat_service.py 是集成测试脚本,但位置和组织方式不符合标准测试规范:

  • 位于项目根目录,不易管理
  • 未使用 pytest 框架
  • 缺少测试分类(单元测试 vs 集成测试)
  • 缺少共享 fixtures 和配置

解决方案

1. 创建标准测试目录结构

server/
├── pytest.ini                    # pytest 配置文件
└── tests/
    ├── __init__.py              # 测试模块初始化
    ├── conftest.py              # pytest 配置和共享 fixtures
    ├── README.md                # 测试文档
    ├── integration/             # 集成测试
    │   ├── __init__.py
    │   ├── test_sms_service.py
    │   └── test_wechat_service.py
    └── unit/                    # 单元测试(预留)
        └── __init__.py

2. 配置 pytest

pytest.ini:

[pytest]
testpaths = tests
python_files = test_*.py
asyncio_mode = auto
log_cli = true
log_cli_level = INFO

markers =
    integration: 集成测试(需要真实数据库、Redis)
    unit: 单元测试(使用 Mock)
    slow: 慢速测试

3. 创建共享 Fixtures

conftest.py:

@pytest.fixture
async def db_session(engine) -> AsyncGenerator[AsyncSession, None]:
    """创建数据库会话(自动回滚)"""
    async_session = sessionmaker(
        engine,
        class_=AsyncSession,
        expire_on_commit=False
    )
    
    async with async_session() as session:
        yield session
        await session.rollback()

@pytest.fixture
async def redis_client() -> AsyncGenerator[Redis, None]:
    """创建 Redis 客户端(自动清理)"""
    client = Redis.from_url(settings.REDIS_URL)
    yield client
    await client.flushdb()
    await client.close()

4. 重构测试文件

改进点:

  • 使用 pytest 装饰器(@pytest.mark.asyncio, @pytest.mark.integration
  • 使用共享 fixtures(db_session, redis_client
  • 拆分为独立的测试函数
  • 添加断言和异常测试
  • 保留直接运行支持(兼容旧方式)

示例:

@pytest.mark.integration
@pytest.mark.asyncio
async def test_send_verification_code(db_session):
    """测试发送验证码"""
    sms_service = SmsService(db_session)
    
    result = await sms_service.send_verification_code(
        phone="15980906230",
        country_code="+86",
        purpose=SmsPurpose.LOGIN,
        ip_address="127.0.0.1"
    )
    
    assert result is not None
    assert "message" in result
    assert result["expiresIn"] == 600

修改文件

创建

  • server/pytest.ini - pytest 配置
  • server/tests/__init__.py - 测试模块初始化
  • server/tests/conftest.py - 共享 fixtures
  • server/tests/README.md - 测试文档
  • server/tests/integration/__init__.py - 集成测试模块
  • server/tests/integration/test_sms_service.py - 短信服务测试(重构)
  • server/tests/integration/test_wechat_service.py - 微信服务测试(重构)
  • server/tests/unit/__init__.py - 单元测试模块(预留)

删除

  • server/test_sms_service.py - 已移动到 tests/integration/
  • server/test_wechat_service.py - 已移动到 tests/integration/

运行测试

使用 pytest(推荐)

# 运行所有测试
pytest tests/ -v

# 仅运行集成测试
pytest tests/integration/ -v

# 运行特定测试文件
pytest tests/integration/test_sms_service.py -v

# 运行特定测试函数
pytest tests/integration/test_sms_service.py::test_send_verification_code -v

# 显示打印输出
pytest tests/ -v -s

# 使用标记
pytest -m integration  # 仅集成测试
pytest -m "not slow"   # 跳过慢速测试

直接运行(兼容旧方式)

# 仍然支持直接运行
python tests/integration/test_sms_service.py
python tests/integration/test_wechat_service.py

测试分类

集成测试(Integration Tests)

  • 位置: tests/integration/
  • 特点: 使用真实数据库、Redis
  • 用途: 验证完整业务流程
  • 标记: @pytest.mark.integration

现有测试:

  • test_sms_service.py - 短信发送、验证、防刷、清理
  • test_wechat_service.py - 二维码生成、登录、绑定、解绑

单元测试(Unit Tests)

  • 位置: tests/unit/(预留)
  • 特点: 使用 Mock/Stub,不依赖外部服务
  • 用途: 测试单个函数或类的逻辑
  • 标记: @pytest.mark.unit

未来计划:

  • Service 层业务逻辑测试
  • Repository 层数据访问测试
  • Utils 工具函数测试

环境要求

集成测试

需要以下服务运行:

# 启动数据库和 Redis
docker-compose up -d jointo-server-db jointo-server-redis

# 运行数据库迁移
docker exec jointo-server-app alembic upgrade head

# 运行测试
pytest tests/integration/ -v

单元测试

不依赖外部服务,可以直接运行:

pytest tests/unit/ -v

优势

1. 标准化

  • 符合 pytest 最佳实践
  • 清晰的目录结构
  • 统一的测试规范

2. 可维护性

  • 共享 fixtures 减少重复代码
  • 独立的测试函数便于调试
  • 清晰的测试分类

3. 可扩展性

  • 预留单元测试目录
  • 支持自定义标记
  • 易于添加新测试

4. CI/CD 友好

  • 标准的 pytest 命令
  • 支持覆盖率报告
  • 支持并行测试

后续计划

  1. 添加单元测试

    • Service 层业务逻辑测试
    • Repository 层数据访问测试
    • Utils 工具函数测试
  2. 添加 API 测试

    • 使用 TestClient 测试 FastAPI 路由
    • 测试请求/响应格式
    • 测试认证和权限
  3. 添加性能测试

    • 使用 pytest-benchmark
    • 测试关键接口性能
    • 监控性能回归
  4. 集成 CI/CD

    • GitHub Actions 自动运行测试
    • 生成覆盖率报告
    • 测试失败时阻止合并

相关文档