2. 测试环境初始化:虚拟环境搭建、依赖管理、conftest.py全局配置

测试环境初始化是授权测试与参数配置的基石。一个隔离、可复现、配置集中的测试环境,能有效避免依赖冲突、环境漂移和配置泄漏。本节将围绕虚拟环境搭建、依赖锁定、以及 conftest.py 的全局夹具与钩子设计展开,确保后续所有测试用例在一致且可控的上下文中执行。

2.1 虚拟环境搭建与依赖管理

推荐使用 venv(Python 3.3+ 内置)或 conda(当项目涉及 C 扩展依赖时)创建隔离环境。以下以 venv 为例,展示标准初始化流程:

# 创建虚拟环境(项目根目录下执行)
python -m venv .venv

# 激活环境(Linux/macOS)
source .venv/bin/activate

# 激活环境(Windows PowerShell)
.venv\Scripts\Activate.ps1

# 安装核心依赖
pip install pytest pytest-xdist pytest-mock requests pyyaml

# 导出依赖锁定文件
pip freeze > requirements.txt
依赖锁定策略: 使用 pip freeze > requirements.txt 锁定所有子依赖版本。对于生产级项目,建议额外使用 pip-tools 或 poetry 管理 requirements.in 与 requirements.txt 的分离,确保可追溯性。

依赖管理的关键在于区分“测试依赖”与“运行依赖”。下表列出本课程场景库的核心依赖分组:

依赖类别 包名 用途 版本建议
测试框架 pytest 测试发现、执行、断言 >=7.4
并行加速 pytest-xdist 多 worker 并行执行测试 >=3.3
Mock 与桩 pytest-mock 轻量级 mock 夹具 >=3.11
HTTP 请求 requests 模拟 API 调用与授权请求 >=2.31
配置解析 pyyaml 读取 YAML 格式的授权策略与参数 >=6.0
数据验证 pydantic 配置模型与参数校验 >=2.5

2.2 conftest.py 全局配置架构

conftest.py 是 pytest 的全局配置入口,位于测试目录根节点。它负责注册夹具(fixture)、钩子函数(hook)和命令行选项。在授权测试场景中,conftest.py 需要承载以下核心职责:

  • 加载全局配置:从 YAML/JSON 文件读取授权端点、密钥、角色映射等敏感参数。
  • 初始化测试客户端:创建带授权头的 HTTP 会话,供所有测试用例复用。
  • 注册授权夹具:提供 auth_token、admin_session、guest_session 等预置上下文。
  • 定义命令行选项:允许通过 --env、--auth-mode 等参数动态切换测试环境。
安全提醒: 切勿将真实密钥、Token 或密码硬编码在 conftest.py 中。应通过环境变量或加密的配置文件注入,并在 .gitignore 中排除敏感文件。

以下是一个典型的 conftest.py 骨架,展示全局夹具与配置加载:

# conftest.py
import pytest
import yaml
import os
from requests import Session

def pytest_addoption(parser):
    """注册自定义命令行选项"""
    parser.addoption(
        "--env",
        action="store",
        default="staging",
        help="指定测试环境: staging, production"
    )
    parser.addoption(
        "--auth-mode",
        action="store",
        default="jwt",
        help="授权模式: jwt, oauth2, apikey"
    )

@pytest.fixture(scope="session")
def config(request):
    """全局配置加载:根据 --env 参数加载对应 YAML 文件"""
    env = request.config.getoption("--env")
    config_path = f"config/{env}_config.yaml"
    if not os.path.exists(config_path):
        raise FileNotFoundError(f"配置文件不存在: {config_path}")
    with open(config_path, "r") as f:
        return yaml.safe_load(f)

@pytest.fixture(scope="session")
def auth_session(config):
    """预授权 HTTP 会话:自动注入 Authorization 头"""
    session = Session()
    auth_mode = config["auth"]["mode"]
    if auth_mode == "jwt":
        token = os.environ.get("TEST_JWT_TOKEN")
        if not token:
            pytest.skip("环境变量 TEST_JWT_TOKEN 未设置")
        session.headers.update({"Authorization": f"Bearer {token}"})
    elif auth_mode == "apikey":
        api_key = config["auth"]["api_key"]
        session.headers.update({"X-API-Key": api_key})
    return session

@pytest.fixture
def admin_session(auth_session, config):
    """管理员会话:在基础会话上添加 admin 角色标识"""
    auth_session.headers.update({"X-Role": "admin"})
    return auth_session

@pytest.fixture
def guest_session(auth_session):
    """访客会话:移除角色标识,模拟未授权用户"""
    auth_session.headers.pop("X-Role", None)
    return auth_session
作用域陷阱: scope="session" 的夹具在整个测试会话中只创建一次,适合加载配置和创建连接池。但若测试需要修改会话状态(如切换 Token),应使用 scope="function" 或通过 request.addfinalizer 清理,避免状态污染。

2.3 全局配置与夹具依赖关系

下图展示了 conftest.py 中夹具的依赖层级与数据流向,帮助理解配置如何从命令行参数传递到具体测试用例:

conftest.py 全局夹具依赖关系图 pytest_addoption --env / --auth-mode config (session) 加载 YAML 配置 auth_session (session) 注入 Authorization 头 admin_session X-Role: admin guest_session 无角色标识 custom_session 自定义角色/Token 测试用例 (test_*.py) 注入 admin_session / guest_session

2.4 环境初始化检查清单

在编写任何授权测试用例之前,请对照以下清单确认环境就绪:

  • 虚拟环境已激活:终端提示符显示 (.venv) 前缀。
  • 依赖已安装:执行 pip list | grep pytest 确认核心包存在。
  • 配置文件就位:config/staging_config.yaml 与 config/production_config.yaml 已创建且包含 auth 字段。
  • 环境变量已设置:TEST_JWT_TOKEN 或对应密钥已通过 .env 文件或 CI 变量注入。
  • conftest.py 位于根测试目录:pytest 能自动发现并加载全局夹具。
  • 首次冒烟测试通过:运行 pytest --env=staging --auth-mode=jwt -k "test_config_loaded" 确认配置加载正常。
快速验证: 在 conftest.py 中添加一个 pytest_configure 钩子,打印当前环境与授权模式,确保启动时可见:
def pytest_configure(config): print(f"\n[环境初始化] env={config.getoption('env')}, auth_mode={config.getoption('auth_mode')}")

2.5 多环境配置结构示例

为了支持不同测试目标(本地开发、CI、预发布),建议采用以下目录结构:

project_root/
├── .venv/                  # 虚拟环境(不提交)
├── config/
│   ├── local_config.yaml   # 本地开发:mock 授权服务
│   ├── staging_config.yaml # 预发布:真实授权端点(测试账号)
│   └── production_config.yaml # 生产:只读 Token,仅用于健康检查
├── tests/
│   ├── conftest.py         # 全局配置与夹具
│   ├── test_auth/          # 授权测试模块
│   └── test_params/        # 参数配置测试模块
├── requirements.txt        # 锁定依赖
└── pytest.ini              # 默认 pytest 配置

pytest.ini 可预设默认参数,减少命令行输入:

[pytest]
addopts = -v --tb=short --env=staging --auth-mode=jwt
testpaths = tests
python_files = test_*.py
markers =
    auth: 授权相关测试
    param: 参数配置测试
    smoke: 冒烟测试,快速验证环境