首页
/ Screenshot to Code 后端测试实战:pytest 运行、配置解析与测试编写规范

Screenshot to Code 后端测试实战:pytest 运行、配置解析与测试编写规范

2026-09-03 17:02:03作者:滕妙奇

本文基于 screenshot-to-code 仓库的 TESTING.md 测试指南展开,完整覆盖后端测试的运行命令(全量、定向、覆盖率、并行)、pytest.ini 中每一项配置的实际含义,并结合 backend/pyproject.toml 的依赖声明与 backend/tests 目录下 40 余个真实测试文件,讲解如何按仓库既有风格为 FastAPI 后端编写新的 pytest 测试。

测试体系概览

Screenshot to Code 的后端是一个 FastAPI 服务(backend/main.py),其测试体系基于 pytest + Poetry 构建:

说明:官方 TESTING.md 的测试指南聚焦后端。前端(frontend/)另有基于 Jest 的独立测试体系(frontend/jest.config.js),不在本文范围内。

前置条件:环境准备

测试运行依赖 Poetry 环境。按文档要求,先安装全部依赖:

cd backend
poetry install

backend/pyproject.toml 可以确认几个关键前提:

配置项 取值 说明
Python 版本 ^3.10 需要 Python 3.10 及以上
测试框架 pytest = "^7.4.3" 位于 dev 依赖组
异步支持 pytest-asyncio = "^0.21" 位于 dev 依赖组,配合 asyncio_mode = auto
类型检查 pyright = "^1.1.352" 用于类型检查,与测试运行相互独立

测试框架与异步插件都声明在 [tool.poetry.group.dev.dependencies] 中,因此 poetry install(默认安装含 dev 组)后即可直接运行测试。此外注意:pytest-xdist 没有被写进 pyproject.toml,如需并行测试要按文档另行安装(见下文)。

运行测试:从全量到定向的完整命令集

运行全部测试

cd backend
poetry run pytest

由于 backend/pytest.ini 中配置了 testpaths = tests,pytest 会自动将收集范围限定在 tests/ 目录,无需在命令行重复指定路径。

更详细的输出

poetry run pytest -vv

-vv 在默认 -v 的基础上再提升一级详细度,适合在调试单个模块时观察用例间的依赖与参数展开。

定向运行:文件 / 类 / 方法三级粒度

pytest 的 :: 选择器支持逐级缩小运行范围,这是日常开发中最常用的三种姿势:

# 只运行某个测试文件
poetry run pytest tests/test_screenshot.py

# 只运行某个测试类
poetry run pytest tests/test_screenshot.py::TestNormalizeUrl

# 只运行某个测试方法
poetry run pytest tests/test_screenshot.py::TestNormalizeUrl::test_url_without_protocol

test_screenshot.py 为例,TestNormalizeUrl 类验证的是 backend/routes/screenshot.pynormalize_url() 的行为:无协议时补全 https://、保留已有的 http/https、对 ftp://file:// 等不支持的协议抛出 ValueError。定位到某一失败用例后,用方法级选择器单独重跑是最快的验证方式。

带覆盖率报告运行

poetry run pytest --cov=routes

--cov 参数指定要统计覆盖率的包。仓库路由层位于 backend/routes/,上例即可输出该目录的覆盖率报告;换成 --cov=agent--cov=prompts 可以分别评估 Agent 引擎和 Prompt 构造层的覆盖情况。注意:pytest-cov 同样属于按需安装的插件,若当前环境未安装需先通过 Poetry 添加。

并行运行(需要 pytest-xdist)

poetry install --with dev pytest-xdist  # Install if not already installed
poetry run pytest -n auto

-n auto 会按 CPU 核心数自动分配 worker。这里要特别留意:pytest-xdist 未包含在 backend/pyproject.toml 的依赖声明中,属于测试时的可选增强。若你使用的 Poetry 版本不识别上述安装语法,等价的标准写法是先以 poetry add --group dev pytest-xdist 将插件加入 dev 依赖组。

测试配置解析:backend/pytest.ini 逐行说明

测试行为由 backend/pytest.ini 统一控制,完整内容如下:

[pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts = -v --tb=short
asyncio_mode = auto

TESTING.md 中列出了前五项的约定,这里逐项补充其实际效果,并补上文档未展开的最后一项:

配置项 作用
testpaths tests 裸跑 pytest 时自动限定收集目录,即 backend/tests/
python_files test_*.py 只收集 test_ 前缀的文件为测试模块
python_classes Test* 只收集 Test 前缀的类为测试类(不带下划线)
python_functions test_* 只收集 test_ 前缀的函数为测试用例
addopts -v --tb=short 默认开启详细输出与短 traceback,无需手动加 -v
asyncio_mode auto pytest-asyncio 自动模式:异步测试函数无需显式装饰即可被正确驱动

关于 asyncio_mode = auto:它来自 pytest-asyncio 插件,开启后所有 async def test_* 函数都会被自动按异步协程处理。值得注意的是,仓库中的实际写法(如 test_websocket_communicator.py)仍然为每个异步用例显式标注了 @pytest.mark.asyncio——两种写法在 auto 模式下都有效,显式标注让意图更清晰,也是本仓库的既有风格。

编写新测试:仓库约定与真实范例

TESTING.md 给出的三步流程依然成立:

  1. backend/tests/ 下按 test_<module>.py 命名约定新建文件;
  2. 导入待测的函数 / 类;
  3. 按 pytest 约定编写测试函数或测试类。

文档中的最小示例:

import pytest
from routes.screenshot import normalize_url

def test_url_normalization():
    assert normalize_url("example.com") == "https://example.com"

这个示例与仓库真实代码完全对得上:normalize_url 定义于 backend/routes/screenshot.py,对应的完整测试类 TestNormalizeUrl 涵盖了无协议补全、协议保留、路径与查询参数、空白裁剪、非法协议报错、localhost 与 IP 地址等 8 组场景。

风格建议:用类组织相关用例

纯函数测试可以用顶层 test_* 函数,但仓库中多数文件采用 Test 类 + 语义化方法名 的组织方式,例如 test_screenshot.py

class TestNormalizeUrl:
    """Test cases for URL normalization functionality."""

    def test_url_without_protocol(self):
        """Test that URLs without protocol get https:// added."""
        assert normalize_url("example.com") == "https://example.com"
        assert normalize_url("www.example.com") == "https://www.example.com"

类 docstring 说明被测功能,方法 docstring 说明具体断言语义,这使 pytest -vv 的输出和失败报告都具备自解释性。

异步组件的测试范式:MagicMock + AsyncMock

后端大量组件是异步的(WebSocket 通信、Provider 会话等),test_websocket_communicator.py 展示了仓库处理这类测试的标准做法——用 MagicMock/AsyncMock 替换 FastAPI/Starlette 的 WebSocket,避免真实网络依赖:

from unittest.mock import AsyncMock, MagicMock
from starlette.websockets import WebSocketDisconnect
from routes.generate_code import WebSocketCommunicator

@pytest.mark.asyncio
async def test_receive_params_marks_websocket_closed_on_disconnect() -> None:
    websocket = MagicMock()
    websocket.receive_json = AsyncMock(side_effect=WebSocketDisconnect(1006))
    communicator = WebSocketCommunicator(cast(Any, websocket))

    with pytest.raises(WebSocketDisconnect):
        await communicator.receive_params()

    assert communicator.is_closed is True

该文件还专门模拟了 "ASGI message 'websocket.close' after response already completed" 这类真实生产中的竞态异常,验证 WebSocketCommunicator 在连接已完成后不会二次抛错(backend/tests/test_websocket_communicator.py)。为异步测试新写用例时,直接参照这一范式即可。

用异常断言覆盖错误路径

pytest.raises 是仓库中验证错误路径的惯用手段,例如 test_screenshot.py 对非法协议的双重断言(同时匹配异常类型和错误消息):

def test_invalid_protocols(self):
    """Test that unsupported protocols raise ValueError."""
    with pytest.raises(ValueError, match="Unsupported protocol: ftp"):
        normalize_url("ftp://example.com")

结语:从源码结构看测试与模块的对应关系

TESTING.md 给出的命令集配合 backend/pytest.ini 的收集约定,构成了一个可直接上手的测试工作流;而 backend/tests/ 目录的组织方式(test_agent_*test_openai_*test_evals_* 等与 agent/routes/evals/ 目录名一一对应)也表明:仓库倾向于按被测模块命名测试文件,新模块的测试可以沿用同样的映射规则。日常开发时建议的闭环是:pytest tests/<文件>::<类>::<方法> 快速复现单个失败 → 修复后用 --cov=<模块> 确认覆盖未回退 → 最后全量 poetry run pytest 保证无回归。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384