1. 测试背景与目标
前两轮已经围绕账号、会话、修改密码,以及角色权限和数据归属进行了测试。本轮进一步验证 OJ 平台的核心业务:
题目查询 → 创建草稿 → 发布前试运行 → 发布题目 → 用户提交 → 查询判题结果 → 查看历史记录。
为了控制项目规模,本轮将题目查询、题目管理、判题和提交记录合并测试,重点验证关键链路及容易影响业务正确性的异常场景,不对每个功能进行穷举。
测试采用Python + Requests + Pytest,在 PyCharm 中执行,通过 pytest-html 生成报告。同时保留请求编号、脱敏响应和测试资源编号,方便失败时定位问题。
本轮共设计并执行24 项自动化测试,测试通过。
2. 测试范围与组织方式
本轮测试分为四组:
| 分组 | 数量 | 主要内容 |
|---|---|---|
| 查询与筛选 | 13 | 列表与详情、关键词、难度、编号、页码及非法参数 |
| 判题结果 | 6 | AC、WA、CE、RE、TLE,以及数组题正常提交 |
| 非法提交 | 3 | 空白代码、代码字段类型错误、版本字段类型错误 |
| 生命周期与历史一致性 | 2 | 试运行发布门禁、版本冲突、题目修改后的历史记录 |
| 合计 | 24 | 覆盖核心业务链路及典型异常 |
这里的“24 项”是 Pytest 收集到的测试数量,不是 HTTP 请求数量。例如,一项题目生命周期测试会连续调用创建、试运行、发布、下架和修改等接口,并包含多个断言。
测试设计主要使用:
- 等价类与边界分析:区分合法查询、非法参数和不同判题结果。
- 状态转换:验证草稿、已发布、已下架之间的业务规则。
- 业务链路关联:将接口返回的题目编号、版本和提交编号用于后续请求。
- 数据一致性检查:确认题目变化不会改写既有提交记录。
3. 测试数据与环境准备
测试对象部署在 Ubuntu 云服务器,包含 Web 服务、异步判题 Worker、编译运行服务和 MySQL。自动化脚本在本地 Windows 的 PyCharm 中运行,通过 HTTP 访问服务器。
为了避免影响正式题库,脚本创建带随机CORE_前缀的专用测试题,并注册独立普通用户。管理员使用已有网站账号登录,用于准备和管理本轮测试资源。
测试结束后,脚本将本轮发布的测试题下架,保留草稿、归档题目和提交记录用于复查,不直接删除数据库数据。
执行前检查以下条件:
- Web、Worker、编译服务和数据库正常运行。
- 本机直连公网 IP 已被云端防火墙放行。
- 测试期间不同时执行压测或其他认证测试。
- 不在浏览器重新登录脚本使用的管理员账号,避免触发单账号会话替换。
这种准备方式既保证测试数据可区分,也减少了对原有题目和账号的影响。
4. 题目查询、搜索与筛选测试
4.1 列表和详情的响应检查
题目列表与详情不仅需要返回 HTTP 200,还应返回正确的数据内容。
本轮检查:
- 列表包含目标已发布题目;
- 页码、每页数量和是否存在下一页等字段符合预期;
- 详情中的标题、版本和初始代码模板与测试题一致;
- 公开详情不包含测试驱动、题目内部快照等字段。
初始代码模板是公开题目功能的一部分,因此不能将详情中出现code字段直接判断为答案泄露。
4.2 搜索与筛选组合
围绕唯一测试题标题,分别验证:
| 场景 | 预期 |
|---|---|
| 按标题搜索 | 能找到目标题目 |
| 关键词前后带空白 | 去除首尾空白后仍能匹配 |
| 关键词结合正确难度 | 返回符合条件的题目 |
| 按题目编号查询 | 结果中包含目标题目 |
| 唯一标题过滤后查询第二页 | 返回空列表,且没有下一页 |
| 关键词结合不匹配的难度 | 返回空列表 |
其中,第二页场景验证的是过滤结果不足一页时的空页行为,没有额外构造 21 条数据进行完整分页容量验证。
4.3 非法查询参数
异常参数覆盖:
- 页码为 0;
- 页码为非数字;
- 页码超过允许范围;
- 重复传入页码;
- 难度值不在允许范围;
- 关键词超过长度限制。
这些请求预期返回 HTTP 400 和INVALID_REQUEST,而不是服务器异常或含义不明确的成功结果。
5. 题目管理与发布前试运行
题目管理涉及多个状态,不能只测试“创建成功”和“发布成功”。
本轮按照以下路径组织测试:
创建草稿 ↓ 未试运行时发布,被拒绝 ↓ 错误答案试运行后发布,仍被拒绝 ↓ 正确答案试运行通过 ↓ 发布成功,公开接口可查询 ↓ 下架并修改题目 ↓ 旧试运行不再满足新版本的发布要求 ↓ 重新试运行通过后发布5.1 草稿不能被公开读取
管理员创建题目后,题目应处于草稿状态。
此时通过公开列表搜索,不应出现该题;直接访问公开详情接口,也应返回题目不可用,而不是泄露草稿内容。
5.2 发布必须经过有效试运行
没有试运行记录时发布,预期返回:
HTTP 409 VALIDATION_REQUIRED使用错误答案试运行后,即使任务正常执行完成,也不能发布,因为结果没有全部通过。
使用正确参考代码试运行,并确认最终结果为 AC 后,才允许发布。
这里需要区分:
- 任务完成:后台已经处理完任务。
- 试运行通过:代码执行结果符合全部用例预期。
任务完成不代表满足发布条件。
5.3 题目修改后需要重新验证
题目下架并修改后,版本会发生变化。旧版本的试运行结果不能继续作为当前版本的发布依据,需要重新试运行。
这一检查避免出现“之前试运行通过,但修改内容后没有重新验证就直接发布”的情况。
6. 版本控制与冲突检查
版本号用于防止客户端使用旧数据覆盖新内容。
例如,两个操作基于相同版本读取题目,其中一个已经保存修改,另一个再使用旧版本提交,就可能覆盖前一个操作。
本轮检查以下规则:
- 已发布题目不能直接编辑,需要先下架;
- 修改成功后版本递增;
- 使用旧版本再次修改,返回
VERSION_CONFLICT; - 被拒绝后,当前标题和版本保持为最近一次成功修改的结果;
- 题目重新发布后,用户携带旧版本提交代码,也应被拒绝。
这些检查同时关注错误响应和资源状态,避免只验证“返回了 409”,却没有确认当前数据是否受到影响。
7. 五类判题结果验证
判题是 OJ 的核心能力。本轮选择一道简单题,通过不同代码样本触发不同结果。
题目要求:返回整数 x 的两倍。
公开用例为:
| 输入 | 期望输出 |
|---|---|
| -3 | -6 |
| 0 | 0 |
| 7 | 14 |
预期值直接由题目规则确定,不使用接口返回值反过来作为测试预期。
7.1 判题样本设计
| 结果 | 含义 | 样本设计 |
|---|---|---|
| AC — Accepted | 答案正确 | 返回x * 2 |
| WA — Wrong Answer | 答案错误 | 固定返回 0 |
| CE — Compilation Error | 编译错误 | 故意缺少分号 |
| RE — Runtime Error | 运行错误 | 调用std::abort() |
| TLE — Time Limit Exceeded | 超过时间限制 | 带 volatile 计数的无限循环 |
对于这些测试,不能只检查判题状态接口是否返回 HTTP 200,还需要检查:
- 任务是否进入预期终态;
- 任务层和结果层的 verdict 是否一致;
- 业务状态值是否符合预期;
- 提交关联的题目版本是否正确。
CE 场景还检查编译诊断不为空,确保用户能够获得错误反馈。
7.2 WA 场景为什么会有部分用例通过
固定返回 0 的错误代码,其预期表现为:
| 输入 | 期望 | 实际 | 是否通过 |
|---|---|---|---|
| -3 | -6 | 0 | 否 |
| 0 | 0 | 0 | 是 |
| 7 | 14 | 0 | 否 |
因此,整体结果应为 WA,但通过数量应为1/3。
自动化不仅断言 WA,还检查总用例数、完成数量、通过数量,以及各用例的输入、期望值、实际值和通过状态。
这能够区分“整体判定正确”和“用例详情统计正确”,避免只检查最终标签而遗漏明细错误。
7.3 补充不同输入类型
除了整数输入,本轮还使用数组求和题验证另一类正常提交:
[1,2,3] → 6[] → 0[-2,5] → 3
这组数据覆盖普通数组、空数组和包含负数的情况,确认判题流程并非只在一种函数输入形式下可用。
8. 异步结果查询与非法提交
8.1 不能把 202 当成判题通过
当前平台采用异步判题。提交请求返回 HTTP 202,表示任务已经受理,不表示答案正确,也不表示后台已经执行完成。
自动化流程为:
提交代码 ↓ 检查 202 和任务编号 ↓ 根据返回地址轮询状态 ↓ 等待终态 ↓ 验证判题结果状态查询可能返回排队中、运行中或终态。任务执行较快时,客户端可能直接观察到终态,因此没有强制要求每次都必须看到中间的 RUNNING。
如果任务最终进入ERROR,即使轮询接口返回 HTTP 200,也不能判为测试通过。
本轮只验证正常业务所需的受理与结果查询,队列持久化、重复请求和服务恢复留到后续异步专项。
8.2 非法提交不能产生新记录
本轮检查三种非法输入:
- 代码只有空白字符;
- 代码字段为数字;
- 题目版本字段为错误类型。
预期返回 HTTP 400 和INVALID_REQUEST。
同时,对比请求前后的个人提交列表,确认这些非法请求没有生成新的可见提交记录。这样可以补充验证“请求被拒绝后没有继续进入正常提交流程”。
9. 历史记录与题目修改的一致性
提交记录应反映提交发生时的情况,不能因为管理员之后修改题目而悄悄变化。
本轮执行以下流程:
- 创建并发布测试题。
- 普通用户提交正确代码,等待 AC。
- 读取提交详情,保存原始信息。
- 管理员下架题目,修改标题和描述。
- 再次读取原提交详情并进行对比。
重点核对:
- 提交编号与代码;
- 提交时的题目标题和版本;
- 判题状态与结果;
- 创建、开始和完成时间。
预期以上历史信息保持不变。
同时,检查状态查询接口不返回提交代码,而详情接口能够返回本人的代码,个人列表中也能够找到该记录。
这个场景从接口表现验证了历史记录的稳定性,没有将其扩大描述为对全部内部快照字段的完整验证。
10. 自动化执行与证据保存
本轮沿用账号模块的测试工程,新增core_tests目录:
core_tests/ ├─ conftest.py ├─ samples.py ├─ workflow.py ├─ test_catalog.py ├─ test_judging.py └─ test_lifecycle.py职责划分如下:
samples.py:维护题目、参考代码、错误代码和公开期望值。workflow.py:封装创建、试运行、发布、提交、轮询与清理流程。conftest.py:准备管理员和普通用户客户端。- 测试文件:按照查询、判题、生命周期组织断言。
执行命令:
python -m pytest core_tests -v -s --base-url http://服务器IP:8080 --html=reports/core.html --self-contained-html --junitxml=reports/core.xml运行过程中保留:
| 证据 | 用途 |
|---|---|
| HTML 报告 | 查看通过、失败和错误情况 |
| 环境与版本记录 | 明确本轮测试对象 |
| 请求编号与脱敏响应 | 与服务器日志关联 |
| 测试题编号和清理状态 | 复查测试资源 |
| 提交编号与终态结果 | 核对异步任务处理结果 |
日志中的单次请求耗时只用于辅助观察,本轮没有据此得出性能结论。
清理过程同样纳入检查。如果测试断言通过,但下架测试题失败,会产生清理阶段错误,需要继续处理,不能只看前面的 PASSED。
11. 测试结果与范围说明
本轮 24 项核心业务接口回归测试通过,覆盖了查询筛选、试运行发布、版本冲突、五类判题结果、非法提交和历史记录一致性。
其中,故意提交的错误代码返回 WA、CE、RE、TLE,属于预期结果,因此对应自动化测试通过。代码答案是否通过,与测试用例是否通过,是两个不同层次的判断。
后续将在这组核心回归基础上,继续验证最小异步专项,并使用 JMeter 开展性能与稳定性测试。