高校教务管理系统 - 软件测试岗位面试题
高校教务管理系统 - 软件测试岗位面试题
本题集基于本项目(Vue + Spring Boot 教务系统)出题,不深挖 Java 八股,聚焦测试思维与项目落地。 技术栈:Spring Boot 2.5.6 / MyBatis / MySQL / Redis / Shiro + JWT / POI / Vue 2.7 + Element UI。 三角色:admin(level=0) / teacher(level=1) / student(level=2)。
一、项目理解
Q1. 简单介绍下这个项目,以及你在其中负责的测试工作。
答:这是一个高校教务管理系统,分学生、教师、管理员三种角色,核心功能包括登录认证、学生信息管理、成绩录入与导入导出、智能排课、选课、教学评估、毕业审核、数据备份与操作日志。后端 Spring Boot + MyBatis + MySQL + Redis,认证用 Shiro + JWT,前端 Vue2 + Element UI。测试工作我负责:需求评审与测试用例设计、接口测试(Postman)、功能与回归测试、结合真实缺陷做缺陷分析、以及登录/权限/成绩导入等核心模块的边界与安全测试。
Q2. 你测试时,会重点关注哪些技术风险点?
答:
- 认证授权:JWT 1 小时过期 + refreshToken 24 小时,level 字段控制角色,易出越权。
- 数据一致性:成绩导入首次直接写入、再次提交走审批,存在状态分支。
- SQL 风险:
teaching_class.major_id大量 NULL,INNER JOIN会丢数据(真实 bug)。 - 并发:选课余量、排课资源抢占。
- Excel 导入:格式校验、成绩范围 0-100、空文件。
- 前端联动:年级→专业→班级级联下拉的加载时序。
Q3. 三种角色的权限是怎么实现的?测试时怎么验证?
答:前端用路由 meta.level 控制菜单与页面可见性,后端用 @UserLevel(roles={...}) 注解 + Shiro 做接口级拦截,JWT payload 里带 level 字段。测试要前后端双重验证:①前端隐藏菜单后,直接访问 URL 仍要被拦;②绕过前端直接调后端接口(带 token)应返回 403。重点测垂直越权(学生调管理员接口)和水平越权(学生 A 查学生 B 的成绩单)。
Q4. 为什么要 Shiro + JWT 一起用?
答:JWT 负责”无状态认证”,token 自带用户信息不查库,适合前后端分离;Shiro 负责”授权”,提供角色/权限注解(@UserLevel)和过滤链,做接口级细粒度控制。两者结合 = 认证靠 JWT、授权靠 Shiro。测试关注点:token 过期/伪造/篡改、refreshToken 续期、登出后旧 token 是否仍可用(本项目无黑名单机制,是风险点)。
二、测试用例设计(结合具体功能)
Q5. 登录功能怎么测?请给出测试点。
答:
- 正常:三角色正确账号密码 + 正确验证码 → 返回 token/refreshToken。
- 账号:不存在、被禁用、level 与所选角色不匹配。
- 密码:错误、为空、含空格、SQL 注入字符(
' or 1=1 --)。 - 验证码:空、错误、过期(>5 分钟)、大小写、重复使用(用一次后应失效)。
- 接口层面:GET
/api/sms/user/login无参、少参、并发重复提交。 - 安全:密码明文走 URL(GET 方式)会被浏览器历史/日志记录,是缺陷。
- 返回值:user 为 null 时前端是否正确提示”账号或密码错误”。
Q6. 验证码功能怎么测?项目里验证码存 Redis 5 分钟,固定码 8888。
答:
- 正常:生成 → 输入正确 → 登录成功后 Redis 中该 uuid 被删除(一次性)。
- 过期:等待 5 分钟后提交 → 提示”验证码已过期”。
- 复用:同一 uuid 提交两次 → 第二次应失败。
- 策略:
captcha.strategy=fixed时固定 8888,需测大小写、配置切换。 - 并发/性能:高并发下 Redis 读写是否正确,uuid 是否唯一(UUID.randomUUID 理论不重复)。
- 安全:验证码不应在响应里明文返回(仅返回 base64 图片)。
Q7. 成绩批量导入功能怎么测?(POST /api/score/import-export/class/{classId}/import)
答:用等价类 + 边界值 + 场景法:
- 等价类:成绩 0-100(有效)、负数、>100、非数字(字母/中文)、空、小数(如 85.5)、科学计数。
- 边界值:0、100、59、60、-1、101。
- 文件格式:.xlsx、.xls、.csv(应拒绝)、空文件、超大文件(50MB 限制)、模板被篡改列顺序。
- 业务规则(判定表):①学生不在教学班 → 跳过;②行政班不在 included_classes → 跳过;③首次提交 → 直接导入;④再次提交 → 走审批;⑤学号为空 → 跳过。
- 并发:两人同时导入同一教学班。
- 结果校验:返回 directCount/approveCount/errorCount 是否与实际一致。
Q8. 成绩字段范围 0-100,边界值怎么取?
答:取 0、1、59、60、99、100 六个有效边界,以及 -1、101 两个无效边界,再加小数边界(如 0.5、99.5)验证是否能存小数。结合导入导出,还要测空值(缺考/未录入)和字符串”null”的处理(项目里大量 !"null".equals(...) 判断,说明踩过坑)。
Q9. 自动排课功能怎么测?这是项目核心难点。
答:
- 硬约束:上午≤4节、下午≤4节、晚上≤4节、周六日不排、连堂节数、周次=ceil(总学时/连堂)且≤16。
- 冲突检测:同一教师/教室/教学班时间冲突是否被正确识别并生成调整方案。
- 方案生成:换教室方案、换时间方案各 ≤3 个,偏爱教室优先。
- 边界:教室容量刚好等于人数、无可用教室、所有时段已满。
- 二次排课:已有冲突时再次排课,方案是否为最新(防脏读,TODOLIST 里专门修过)。
- 数据一致性:applyProposal 后 conflictList 是否同步更新。
Q10. “年级→专业→班级”三级联动下拉怎么测?
答(对应 bug 文件夹里”班级下拉显示 ID”的真实 bug):
- 加载时序:弹窗打开即加载 vs 依赖用户操作才加载,测首次打开是否正常。
- 联动:改年级→专业清空重载;改专业→班级清空重载;反向回退是否残留旧值。
- 空数据:某年级无专业、某专业无班级时下拉是否空且不报错。
- 显示:下拉显示的是名称(如”软件工程1班”)而非主键 ID。
- 编辑回显:打开已有记录的下拉默认选中正确项。
Q11. 成绩统计分析功能怎么测?(对应 bug3:返回 0 条数据)
答:
- 正常:有数据时按班级/课程/学年维度统计,图表(ECharts)渲染正确。
- 空数据:教学班无成绩录入时,接口返回 0 条,前端是显示”暂无数据”还是报错。
- 边界:只有 1 条成绩、全部缺考、成绩全相同(方差为 0)。
- 筛选组合(正交法):学年 × 学期 × 年级 × 教学班 多条件交叉。
- 数据准确性:平均分、最高分、及格率、绩点计算与数据库手动核算一致。
三、缺陷分析(结合 bug 文件夹真实 bug)
Q12. 【bug1】编辑学生弹窗首次打开班级下拉显示 ID 而非名称,再打开又正常了。这是什么类型的 bug?怎么定位?
答:属于状态依赖/时序 bug。根因:编辑模式 init 里调了 loadMajorList() 但没调 loadClassList(),classOptions 为空,el-select 找不到匹配项就显示绑定的 value(数字 ID)。再打开时上一次异步请求的数据残留,看起来”正常”了。定位思路:①F12 看 classOptions 是否为空;②看 init 方法是否调了 loadClassList;③对比首次与二次打开的网络请求差异。
Q13. 【bug1】这种”首次异常、再次正常”的 bug,用什么测试方法能提前发现?
答:场景法 + 状态迁移法。等价类边界值测不出这类 bug,因为它依赖”组件是否首次初始化”。测试用例要覆盖:①首次打开弹窗(冷启动);②关闭再打开(数据残留);③切换专业后打开;④刷新页面后首次打开。核心是模拟真实操作路径,而不是孤立测单个动作。回归时要把”冷启动”作为固定用例。
Q14. 【bug2】teaching_class.major_id 为 NULL 时,INNER JOIN profession 导致查询返回空。这属于什么类型 bug?怎么测?
答:属于数据驱动 bug,根因是 NULL 值在 INNER JOIN 时被过滤。测试方法:数据驱动测试,准备多种数据状态——major_id 为 NULL、为单个 ID、为逗号串 '1,2,3'、为不存在的 ID。用例要覆盖”脏数据”场景,不能只用理想数据。修复方案是去掉 JOIN 或改 LEFT JOIN,回归时要验证 NULL 数据能正常返回且不影响有 major_id 的记录。
Q15. 描述一个 bug 的完整生命周期。
答:新建(New)→ 指派(Assigned)→ 开发修复(Fixed)→ 测试回归(Verified)→ 关闭(Closed)。若回归不通过则 Reopen。还有:拒绝(Rejected,非 bug)、延期(Deferred)。本项目流程:发现 bug → 写 md 归档(bug 文件夹)→ 含摘要/根因/复现/修复方案 → 修复后回归 → 关闭。
Q16. bug 的”严重程度”和”优先级”有什么区别?结合本项目举例。
答:严重程度 = 对系统的影响程度;优先级 = 修复的紧迫性。两者不一定一致。
- 成绩导入接口 500 错误:严重度高、优先级高。
- 班级下拉显示 ID(bug1):严重度中(功能可用只是显示丑)、优先级中。
- 某处文案错别字:严重度低、优先级低。
- 严重的偶发崩溃:严重度高,但若只在极端环境复现,优先级可能中。
四、接口测试
Q17. 怎么对这个项目做接口测试?
答:
- 梳理接口清单:按模块(User/Score/Schedule/Enrollment 等)整理 URL、方法、入参、出参、权限。
- 环境准备:本地起后端 9121、MySQL、Redis,准备测试账号(三角色各一)。
- 用 Postman:建集合 → 设环境变量(token/baseURL)→ 登录用例自动提取 token 写入变量 → 后续用例引用
{{token}}。 - 覆盖维度:正常、异常入参、边界值、权限(越权)、幂等性。
- 断言:校验 HTTP 状态码 + 业务 code + 关键字段(如 directCount)。
- 批量执行:用 Newman 命令行跑,接入 CI。
Q18. 登录接口用 @GetMapping("/login") 传账号密码,从测试角度有什么问题?
答:安全缺陷。GET 参数拼在 URL 上,会被浏览器历史、服务器访问日志、代理缓存、Referer 头记录,密码明文泄露风险高。应改为 POST + 请求体。测试时要点出该风险并提缺陷;同时验证 HTTPS 下是否仍走 URL(即便加密通道,日志仍会记录)。这是典型的”测试反推设计”案例。
Q19. 成绩导入接口的幂等性怎么测?
答:幂等 = 同一请求重复执行结果一致。本项目设计:首次提交直接导入,再次提交走审批,不是技术幂等而是业务幂等。测试:①同一 Excel 导入两次,第二次应全部进审批流而非重复写入;②导入中途失败重试,不应产生重复成绩;③同一学号一条记录,不出现两条 score。验证手段:导入前后 SELECT count(*) FROM score_entry 对比。
Q20. token 过期和刷新机制怎么测?
答:
- 过期:token 1 小时后过期,接口返回 401,前端 axios 拦截器跳登录。
- 刷新:refreshToken(24 小时)未过期时,应能用它换新 token,用户无感。
- 都过期:refreshToken 也过期 → 跳登录。
- 伪造:篡改 token payload(如改 level 从 2→0)→ 验签失败 401。
- 登出后:本项目无黑名单,旧 token 在过期前仍可用,是风险点,应提缺陷。
五、安全测试
Q21. 这个项目你做安全测试会关注哪些点?
答:
- 越权:水平(学生 A 查 B)+ 垂直(学生调管理员接口)。
- 认证:JWT 密钥硬编码、token 伪造/过期、登出未失效。
- 注入:SQL 注入(MyBatis
${}而非#{}的地方)、XSS(评教/备注字段)。 - 敏感信息泄露:异常信息回显 SQL/Redis 错误、密码明文 URL、
application.yml明文存 DB/Redis 密码。 - 文件上传:成绩导入只接受 .xlsx/.xls,防可执行文件。
- 验证码:可被绕过、固定码 8888 在生产是否关闭。
- CORS:
withCredentials=true下 Origin 是否受限。
Q22. JWT 密钥硬编码 "sms-panovo" 有什么风险?怎么验证?
答:密钥写死在 JwtUtils.java 源码里,一旦源码泄露(如上传到公开 Git 仓库),攻击者可用该密钥伪造任意用户 token(包括 level=0 的管理员),直接接管系统。验证方式:用该密钥本地签发一个 level=0 的 token,调管理员接口,若成功则确认可伪造。修复:密钥放环境变量/配置中心,定期轮换,源码不提交。
Q23. 全局异常处理器把 SQL/Redis 错误返回给前端,有什么问题?
答:GlobalExceptionHandler 在开发模式下把 Redis连接失败: ...、SQL 语法错误等信息塞进 message 返回。风险:信息泄露,攻击者可据错误信息推断表结构、技术栈、连接配置,辅助进一步攻击。测试应验证生产环境是否屏蔽明细(仅返回”服务异常”)。这也是安全测试用例之一。
Q24. 越权测试怎么设计?举本项目两个例子。
答:
- 水平越权:导出成绩单接口
GET /api/score/import-export/student/{studentId}/export,用学生 A 的 token 把 studentId 改成 B,若能拿到 B 的成绩单则越权。 - 垂直越权:学生 token(level=2)直接访问
/api/sms/user/admin/reset-password(@UserLevel(0)),应返回 403;若能重置他人密码则严重越权。 - 测试方法:抓包改 token / 改 path 参数 / 改 level 字段,对比预期。
六、性能测试
Q25. 哪些场景需要做性能测试?
答:高频、高并发、大数据量、对响应敏感的场景:
- 登录:开学季集中登录。
- 选课:抢课瞬时高并发,余量超卖是经典问题。
- 成绩批量导入:大文件、万级数据。
- 自动排课:算法复杂度高,单次排课耗时长。
- 成绩统计分析:聚合查询,大数据量慢查询。
- 首页 Dashboard:缓存命中率影响响应。
Q26. 成绩批量导入 10000 条,怎么测性能?
答:用 JMeter:
- 构造 10000 行 Excel,单用户上传,测响应时间与内存占用(POI XSSFWorkbook 全量加载易 OOM)。
- 并发 10/50 用户同时导入不同教学班,测吞吐量 TPS。
- 监控后端:CPU、内存、GC、DB 连接池、Redis。
- 断言:导入条数 = 10000,无重复、无丢失,DB 落库一致。
- 瓶颈定位:若 OOM → 建议改 SAX 流式解析;若 DB 慢 → 批量 upsert 改 batch。
Q27. 排课接口响应慢,你怎么排查?
答:
- 定位阶段:后端日志 + APM 看 Controller/Service/Mapper 各段耗时。
- SQL 层:开 MyBatis debug(项目已配
mapper: debug),看是否有 N+1 查询、全表扫描、缺索引。 - 算法层:排课有穷举逻辑,看时间槽/教室组合数量是否爆炸,
findFirstConflict是否做了无效遍历(TODOLIST 提过)。 - 缓存:高频查询是否走了 Redis。
- DB 层:连接池是否打满、慢查询日志。
- 复现后给出优化建议并回归。
七、工具与自动化
Q28. 你用过哪些测试工具?分别用在什么场景?
答:
- Postman / Newman:接口测试、token 自动续期、集合批量跑、CI 集成。
- JMeter:性能/压力测试,选课并发、导入性能。
- Chrome DevTools / Fiddler:前端调试、抓包改包、越权测试。
- Selenium / Cypress:UI 自动化(本项目 Element UI 适合)。
- MySQL 客户端 + Redis CLI:数据校验。
- Git:用例与 bug 文档版本管理。
Q29. 怎么用 Postman 实现 token 自动续期,避免每次手动改?
答:在登录用例的 Tests 脚本里 pm.environment.set('token', responseBody.token),把 token 存入环境变量。其他用例 Header 里写 Authorization: {{token}}。token 过期返回 401 时,可在集合级前置脚本里判断 refreshToken,自动调刷新接口换新 token 再重发。本项目 axios 已实现类似逻辑,Postman 里复刻即可。
Q30. 这个项目适合做 UI 自动化吗?选型怎么考虑?
答:适合。理由:①Vue + Element UI 组件稳定,定位器(id/role)友好;②有大量表单与列表的重复操作(增删改查、导入导出)。选型:Cypress(前端友好、调试快)或 Selenium + Python(生态成熟)。建议只自动化稳定、重复执行、回归价值高的用例(如登录、成绩 CRUD、权限校验),不追求全自动化。Element UI 用 cy.get 或 Select 选择器处理下拉。
八、测试理论(结合场景)
Q31. 黑盒、白盒、灰盒分别是什么?结合本项目说明。
答:
- 黑盒:不看代码,按需求测输入输出。如测成绩导入只看 Excel → 结果。
- 白盒:基于代码结构测,覆盖分支/语句。如看
importScores的 if/else 分支设计用例覆盖每条路径。 - 灰盒:结合两者,看接口/数据流但不深入实现。如测成绩导出时查 DB 落库、看接口返回 JSON 字段。 本项目我做的是灰盒为主:读 Controller/SQL 理解逻辑,再设计黑盒用例,效率最高。
Q32. 回归测试怎么做?结合排课 bugfix 说明。
答:回归 = 修复后验证修复正确且未引入新问题。本项目排课模块改动频繁(TODOLIST 记录多次重构),回归策略:
- 确认修复:用原 bug 的复现用例验证已修复。
- 关联回归:排课涉及冲突检测、方案生成、数据一致性,相关用例全跑。
- 核心回归集:把登录、排课、成绩导入等核心用例固化为自动化集,每次提测自动跑。
- 全量回归:大版本上线前全量跑。 优先级:修复点 > 关联模块 > 核心链路 > 全量。
Q33. 冒烟测试怎么做?本项目冒烟范围是?
答:冒烟 = 提测后快速验证核心链路通不通,不通直接打回。本项目冒烟用例:
- 三角色登录成功返回 token。
- 学生查询成绩列表、教师导出教学班成绩、管理员新增学生。
- 自动排课能跑通并生成课表。
- 成绩导入一个正常 Excel 成功。 5 个用例 5 分钟跑完,全绿才进入详细测试。
九、场景开放题
Q34. 上线后发现成绩统计页面报 500,你的排查思路?
答:
- 复现:确认是必现还是偶发,记录操作路径与参数(学年/学期/教学班)。
- 看日志:后端日志定位异常栈,结合
GlobalExceptionHandler看是 SQL/Redis/空指针。 - 看数据:检查该教学班成绩数据是否异常(NULL、脏数据、关联缺失),参考 bug2 的 NULL JOIN 思路。
- 看接口:Postman 直接调
ScoreAnalysis接口,看返回与耗时。 - 定位根因:SQL/数据/代码三层判断。
- 修复回归:修复后补回归用例(覆盖触发该 bug 的数据状态),防复发。
Q35. 如何保证测试覆盖率?
答:
- 需求覆盖:需求文档每条都有用例对应,用追溯矩阵。
- 代码覆盖:核心模块(登录、成绩导入、排课)读代码做分支覆盖,补漏分支用例。
- 场景覆盖:用场景法覆盖真实操作路径,不只测单功能。
- 数据覆盖:有效/无效/边界/脏数据(NULL、空串、“null”字符串)。
- 回归集 + 缺陷反哺:每个修复的 bug 补一条用例进回归集。
- 度量:用例数 / 需求数、缺陷率、漏测率复盘改进。
Q36. 你觉得这个项目还有哪些可以改进的测试点 / 风险?
答:
- 安全:JWT 密钥硬编码、GET 传密码、异常明细回显、无 token 黑名单(登出可重放)。
- 配置:
application.yml明文存 DB/Redis 密码、固定验证码 8888 需生产关闭。 - 健壮性:Excel 导入对模板格式强依赖、POI 全量加载大文件易 OOM。
- 并发:选课余量无锁可能超卖、排课资源抢占。
- 可测性:建议加接口文档(Swagger)、单元测试、日志规范,便于测试与排障。
附:高频”自我介绍 + 项目”模板
面试官常问”讲一个你测过的印象最深的 bug”——建议用 bug1(班级下拉显示 ID): 现象首次异常再开正常 → 看似随机 → 定位到 init 方法漏调 loadClassList → 异步时序问题 → 用场景法补”冷启动”用例进回归集 → 举一反三排查所有级联下拉。这个故事完整覆盖了现象→定位→根因→测试方法→回归预防,是测试岗高分回答结构。
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!