☰
青砚 OJ 测试实践(三):核心业务接口回归——从题目发布到判题与历史记录
2026/11/2 15:26:06 网站建设 项目流程

1. 测试背景与目标

前两轮已经围绕账号、会话、修改密码,以及角色权限和数据归属进行了测试。本轮进一步验证 OJ 平台的核心业务:

题目查询 → 创建草稿 → 发布前试运行 → 发布题目 → 用户提交 → 查询判题结果 → 查看历史记录。

为了控制项目规模,本轮将题目查询、题目管理、判题和提交记录合并测试,重点验证关键链路及容易影响业务正确性的异常场景,不对每个功能进行穷举。

测试采用Python + Requests + Pytest,在 PyCharm 中执行,通过 pytest-html 生成报告。同时保留请求编号、脱敏响应和测试资源编号,方便失败时定位问题。

本轮共设计并执行24 项自动化测试,测试通过。

2. 测试范围与组织方式

本轮测试分为四组:

分组数量主要内容
查询与筛选13列表与详情、关键词、难度、编号、页码及非法参数
判题结果6AC、WA、CE、RE、TLE,以及数组题正常提交
非法提交3空白代码、代码字段类型错误、版本字段类型错误
生命周期与历史一致性2试运行发布门禁、版本冲突、题目修改后的历史记录
合计24覆盖核心业务链路及典型异常

这里的“24 项”是 Pytest 收集到的测试数量,不是 HTTP 请求数量。例如,一项题目生命周期测试会连续调用创建、试运行、发布、下架和修改等接口,并包含多个断言。

测试设计主要使用:

  • 等价类与边界分析:区分合法查询、非法参数和不同判题结果。
  • 状态转换:验证草稿、已发布、已下架之间的业务规则。
  • 业务链路关联:将接口返回的题目编号、版本和提交编号用于后续请求。
  • 数据一致性检查:确认题目变化不会改写既有提交记录。

3. 测试数据与环境准备

测试对象部署在 Ubuntu 云服务器,包含 Web 服务、异步判题 Worker、编译运行服务和 MySQL。自动化脚本在本地 Windows 的 PyCharm 中运行,通过 HTTP 访问服务器。

为了避免影响正式题库,脚本创建带随机CORE_前缀的专用测试题,并注册独立普通用户。管理员使用已有网站账号登录,用于准备和管理本轮测试资源。

测试结束后,脚本将本轮发布的测试题下架,保留草稿、归档题目和提交记录用于复查,不直接删除数据库数据。

执行前检查以下条件:

  1. Web、Worker、编译服务和数据库正常运行。
  2. 本机直连公网 IP 已被云端防火墙放行。
  3. 测试期间不同时执行压测或其他认证测试。
  4. 不在浏览器重新登录脚本使用的管理员账号,避免触发单账号会话替换。

这种准备方式既保证测试数据可区分,也减少了对原有题目和账号的影响。

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
00
714

预期值直接由题目规则确定,不使用接口返回值反过来作为测试预期。

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-60否
000是
7140否

因此,整体结果应为 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. 历史记录与题目修改的一致性

提交记录应反映提交发生时的情况,不能因为管理员之后修改题目而悄悄变化。

本轮执行以下流程:

  1. 创建并发布测试题。
  2. 普通用户提交正确代码,等待 AC。
  3. 读取提交详情,保存原始信息。
  4. 管理员下架题目,修改标题和描述。
  5. 再次读取原提交详情并进行对比。

重点核对:

  • 提交编号与代码;
  • 提交时的题目标题和版本;
  • 判题状态与结果;
  • 创建、开始和完成时间。

预期以上历史信息保持不变。

同时,检查状态查询接口不返回提交代码,而详情接口能够返回本人的代码,个人列表中也能够找到该记录。

这个场景从接口表现验证了历史记录的稳定性,没有将其扩大描述为对全部内部快照字段的完整验证。

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 开展性能与稳定性测试。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询