2026-07-14-mcp-python-coverage-100-design.md 4.9 KB

MCP Python Gateway 100% 覆盖率设计

背景

Python Gateway 当前执行:

python -m coverage run -m unittest discover -s tests -p "test_*.py"
python -m coverage report -m

全量 297 个测试通过,但 .coveragerc 启用了语句和分支覆盖,当前 TOTAL 仅为 93.63%。报告包含 1153 条语句和 418 条分支,其中缺少 52 条语句、48 条分支, 共有 9 个生产文件未达到 100%。

目标

  • 保持现有 .coveragercsourceomitbranch=True 和报告口径不变。
  • 所有纳入统计的非空生产文件逐个达到 100.00%
  • TOTAL 达到 100.00%,并通过 coverage report --fail-under=100
  • 全量 unittest 保持通过,测试不依赖真实 Redis、外部 HTTP 服务或长期运行线程。
  • 不覆盖、不回退当前工作区已有的单号元数据和测试改动。

不采用的方案

扩大 omit 或添加 pragma

不通过 .coveragerc omitexclude_lines# pragma: no cover 隐藏当前生产代码 缺口。这会改变统计边界,无法证明现有代码路径经过测试。

为覆盖率改写业务逻辑

当前函数级报告未发现必须重构才能触达的代码。默认只修改测试;若实施中证明某条 分支结构上不可达,应暂停并重新评审,而不是直接修改生产逻辑或排除该分支。

实施结构

第一批:叶子模块和数据边界

生产文件 测试文件 覆盖重点
tools/query_order_exact.py tests/test_query_order_exact_tool.py 非法 scalar ID、逗号字符串 ID 列表、字符串列表去重回边
services/token_store.py tests/test_token_store_coverage.py 无目录文件、延迟读取、已删除文件清理、bytes 请求、非法 Redis 行
services/gateway_session_store.py tests/test_gateway_session_store_unit.py 不存在 session 的 touch
config.py tests/test_config_compat.py timeout 环境变量优先级、无效 dotenv 行、带引号值和循环回边

这一批不改生产代码,只补可观察的输入、返回值、异常和依赖调用断言。

第二批:Gateway 应用和 CLI

生产文件 测试文件 覆盖重点
app.py tests/test_app_coverage.py 注册工具名称、动态列表错误、request ID、缺少刷新客户端、public 启动、CLI 必填/可选参数及最终防御分支

serve-public 使用 mock 替换 Redis client、session store、public app 和 server 入口; CLI 防御分支通过模拟参数解析结果触发,不启动真实服务。

第三批:协议与公网服务

生产文件 测试文件 覆盖重点
mcp_protocol.py tests/test_mcp_protocol_coverage.py 无 flush 输出、无 input schema、工具响应数据形态、错误 data/meta、空轨迹 tips
public_gateway.py tests/test_public_gateway_unit.py 动态列表无效响应、不支持 touch_session 的 store
public_server.py tests/test_public_server_coverage.py input schema 归一化、启用限流、清理线程、KeyboardInterrupt 关闭

HTTP server、线程和无限清理循环都使用 mock 控制。清理循环测试捕获 threading.Thread 的 target,并让受控 time.sleep 抛出测试专用异常终止循环,不做 真实等待。

测试质量要求

  • 测试名称描述具体行为,不以“覆盖第 N 行”作为唯一目的。
  • 每个异常分支同时断言异常类型和关键消息。
  • 每个成功分支断言返回值或依赖调用参数,不能只调用代码而不验证结果。
  • mock 仅用于 Redis、socket、HTTP server、线程、时钟和 CLI 解析等外部边界。
  • 不添加只为测试存在的生产 API。
  • 每批完成后运行目标测试,再重新执行全量 coverage,依据新的 Missing 列继续收敛。

验证流程

每批执行:

python -m unittest <本批目标测试模块>
python -m coverage run -m unittest discover -s tests -p "test_*.py"
python -m coverage report -m

最终验收:

python -m coverage run -m unittest discover -s tests -p "test_*.py"
python -m coverage report -m --fail-under=100
git diff --check

验收标准:

  1. 全量 unittest 返回 0。
  2. 报告中每个非空生产文件为 100.00%
  3. TOTAL 为 100.00%,Missing 为空。
  4. .coveragerc 的统计范围和分支覆盖设置未被放宽。
  5. 工作区原有单号工具改动保持完整。

风险与处理

  • 覆盖率 100% 不等于业务逻辑绝对正确;测试仍以可观察行为和关键错误边界为主。
  • 线程/服务器测试若泄漏真实后台线程会导致套件不稳定,因此必须 mock Thread.start 或捕获 target 后同步执行。
  • 分支覆盖可能在新增测试后暴露新的回边提示;以最新 coverage report -m 为准逐项 处理,不通过删除有效断言或放宽配置收尾。