软件测试课程设计:Javaweb+MySQL+LoadRunner压测全链路
简介:面向软件测试与Java Web课程设计的学习者,这套材料以网上书店前台系统为被测对象,串联白盒、黑盒、单元、功能与性能测试的完整实践过程,既可作为课程设计参考,也适合准备测试岗位面试的初学者查漏补缺。压缩包共904个文件、约45.97MB,含JSP页面、Java源码、编译类文件,以及XML配置、JAR依赖和GIF、JPG、PNG界面素材,便于在IDEA中还原项目并结合MySQL开展测试。已有2351人学习。可据此练习逻辑覆盖与基本路径覆盖用例设计,运用等价类、边界值分析完成黑盒测试,并用JUnit验证核心方法;性能部分围绕LoadRunner的稳定性与压力测试展开,从需求理解、用例编写到执行记录均有对应素材,适合课程设计、实验报告与测试入门复盘。
1. 软件测试课程设计:从被测系统到 LoadRunner 压测报告的一条完整链路
很多人做软件测试课程设计,第一反应是随便找个开源系统跑两遍功能点,截几张图交差。真到答辩现场,导师问一句“你这个并发数是怎么定的、响应时间卡在哪个阈值算通过”,当场就答不上来。这套方案要解决的不是“测没测”,而是把被测系统、测试用例、缺陷记录、性能脚本、结果分析串成一条能自证逻辑的链路:被测对象是一个 Javaweb + MySQL 的模拟业务系统,功能测试走黑盒用例设计,性能测试用 LoadRunner 在 IDEA 里编排脚本,最后落成一份带数据支撑的课程设计报告。适合正在做软件测试课设、毕设,或者要从零搭一套可复现测试流程的人。
2. 需求与技术选型:为什么被测系统选 Javaweb + MySQL,压测工具选 LoadRunner
课程设计最容易翻车的地方不是测试执行,而是前期选型没想清楚。被测系统太简单,用例设计展不开;太复杂,环境搭三天还没跑起来。压测工具选错,脚本录不进去,报告里连一份像样的并发曲线都拿不出来。这一章先把两件事定死:被测系统长什么样,工具链为什么这么配。
2.1 被测系统的业务边界与模块划分
被测对象通常做成一个典型的三层 Javaweb 应用,前端 JSP 或静态页面,后端 Servlet / SpringMVC 处理请求,MySQL 落数据,部署在 Tomcat 上。业务别贪多,课程设计的价值在于测试覆盖面,不在于功能量,一般留四到六个模块足够撑起用例设计:
| 模块 | 核心功能 | 测试关注点 |
|---|---|---|
| 用户模块 | 注册、登录、个人信息修改 | 输入校验、重复注册、会话保持 |
| 商品模块 | 列表查询、关键字搜索、详情页 | 分页边界、模糊查询、空结果 |
| 订单模块 | 下单、取消、订单列表 | 金额计算、状态流转、并发下单 |
| 后台模块 | 商品维护、订单审核 | 权限隔离、越权访问 |
这套划分的好处是每个模块都能同时出功能用例和性能场景。商品列表适合做高并发读,订单提交适合做写并发,后台模块天然带权限测试点。表结构不用做深度范式设计,但主键、外键、索引要建,否则后面压测时数据库先成为瓶颈,你测出来的响应时间跟应用层没关系,这条数据在报告里没法解释。
2.2 工具链分工:LoadRunner 管性能,IDEA 管脚本开发与调试
整套工具链的职责要分清楚,不然后面会互相打架:
- IDEA:既用来开发被测系统,也用来写和调试 LoadRunner 的 Java 脚本(Vuser Script)。LoadRunner 支持 Java Vuser,脚本本质是一段带
Actions方法的 Java 类,在 IDEA 里断点调试比在录制器里改要顺手得多。 - LoadRunner:负责脚本回放、场景设计、并发调度、结果采集。Controller 定并发模型,Analysis 出图和报告。
- MySQL:被测数据源,同时要准备测试数据集,避免拿空表压测。
- 浏览器开发者工具:抓请求、看响应、核参数,功能测试和脚本录制都靠它打底。
通常的做法是先用浏览器把关键业务流程走一遍,抓出请求参数和依赖关系(比如登录后的 sessionId、下单前的商品 id),再决定脚本是录制生成还是手写。手写脚本在课程设计里更可控,参数化、关联、检查点都能自己说明白,答辩时也讲得清每个函数在干什么。
2.3 环境与参数基线:把“能跑”变成“可复现”
环境必须写进报告,否则别人复现不了。基线一般这么定:
# 环境基线示例(写进课设报告的环境说明章节) JDK 1.8 Tomcat 8.5 MySQL 5.7 LoadRunner 12.x(Community 版即可满足课设) 浏览器 Chrome,关闭缓存 测试机 同一台机器或同网段两台,避免网络抖动干扰响应时间关键点在于压测机与被测服务尽量分开,哪怕是两台普通机器。同机压测时 CPU 互相抢占,你会看到响应时间随并发数剧烈抖动,这种数据写进报告只会给自己挖坑。如果只有一台机器,就在报告里明确说明这是单机环境、结论只作为相对趋势参考,不要写成绝对值结论。这一句说明,往往就是答辩时被追问的“数据可信度”问题。
3. 测试设计:用例怎么出、缺陷怎么记、性能场景怎么定
设计章节是整份课设的骨架。功能测试给了覆盖面,性能测试给了深度,两者都要有明确的“为什么这么设计”的说明。只贴用例表不讲设计方法,报告会显得很薄;只讲方法论不给表,又落不了地。
3.1 功能测试用例设计:等价类、边界值、场景法三件套
用例设计按模块推进,每个模块至少覆盖正常流、异常流、边界三类。以用户登录为例,设计思路是这样的:
- 等价类:合法账号密码、不存在账号、密码错误、账号为空、密码为空。
- 边界值:用户名长度取最小值、最大值、最大值 +1;密码同理。
- 场景法:连续输错三次后是否锁定、登录后会话是否保持、直接访问内页是否被拦截。
用例表字段固定为:用例编号、模块、前置条件、操作步骤、输入数据、预期结果、实际结果、是否通过。这套字段看着普通,但少了“前置条件”和“输入数据”,用例别人跑不了,也就失去了复现价值。用例编号建议按TC-模块缩写-序号编,比如TC-LOGIN-003,后面缺陷单里引用起来方便。
提示:用例不要只写“输入正确密码,登录成功”这种一句话,操作步骤要写到具体点哪个输入框、点哪个按钮。答辩时导师随机抽一条让你现场演示,步骤写不清楚就是事故。
3.2 缺陷记录与追踪:缺陷单要能独立复现
缺陷记录用一张表管起来,字段建议包括:缺陷编号、关联用例、严重级别、缺陷描述、复现步骤、期望结果、实际结果、状态、修复验证结果。严重级别按业务影响分:阻断(核心流程不可用)、严重(主要功能错误)、一般(次要功能或界面问题)、建议(体验优化)。
真正拉开水平差距的是缺陷描述的写法。反面写法是“登录有问题”,正面写法是“使用已注册账号,密码输入正确但提示密码错误;同一账号在另一浏览器可正常登录,怀疑与缓存或会话有关”。后者带复现条件和初步定位方向,导师一眼能看出你是真的测过。测试执行完毕要有一张通过率统计表,按模块汇总用例数、通过数、失败数、缺陷数,这张表是报告的收口数据。
3.3 性能场景设计:并发数、业务比例、监控指标怎么定
性能测试不是把并发数拉到最大跑一次就完事。课程设计里最合理的做法是设计三个场景:
| 场景 | 并发用户数 | 目标 | 说明 |
|---|---|---|---|
| 基准测试 | 1 / 10 | 建立单用户、小并发基线 | 作为后续对比参照 |
| 负载测试 | 逐步加压至 100 | 找系统稳定承载区间 | 观察响应时间与错误率拐点 |
| 压力测试 | 超出承载区间 | 观察系统崩溃表现与恢复能力 | 不作为通过标准,只作分析 |
指标上关注三件事:平均响应时间、TPS(每秒事务数)、错误率。通常给负载测试定一个经验门槛,比如平均响应时间不超过 3 秒、错误率低于 1%,这个门槛要写进报告作为“验收标准”,而不是测完之后再补一个说法。并发数的确定要有依据:结合被测业务的实际用户规模估算,或者按“服务器 CPU 达到 70% 左右时的并发数”作为参考承载点,两种说法都行,但必须写清楚依据,不能凭空写个 100。
4. 落地实现:LoadRunner Java 脚本编写与参数化关联
这一章是技术密度最高的部分。脚本能不能跑通、参数化做没做、关联对不对,直接决定压测结果有没有意义。用 IDEA 写 Java Vuser 脚本的最大好处是可控:每一行都能解释给别人听。
4.1 脚本骨架:从 Actions 方法到事务包裹
LoadRunner 的 Java Vuser 脚本按init、action、end三段组织。init放登录等一次性操作,action放被测业务,end放退出清理。事务点用lr.start_transaction和lr.end_transaction包住,只有包住的这段才会被统计成响应时间。
import lrapi.lr; import lrapi.web; public class Actions { // 初始化:每个虚拟用户只执行一次 public int init() throws Throwable { web.url("首页", "http://localhost:8080/shop/index.jsp"); web.url("登录", "http://localhost:8080/shop/login?username={username}&password={password}"); return 0; } // 业务动作:按迭代次数重复执行 public int action() throws Throwable { // 事务开始:只统计商品查询这一段 lr.start_transaction("查询商品列表"); web.url("商品列表", "http://localhost:8080/shop/product/list?page={pageNo}"); web.reg_find("检查列表返回", "Text=商品名称", web.LR_LAST); lr.end_transaction("查询商品列表", lr.AUTO); return 0; } public int end() throws Throwable { return 0; } }几个点必须说明清楚。web.reg_find是检查点,用来确认返回内容里确实包含关键文本,没有检查点的脚本跑完“全部成功”也没有说服力。lr.AUTO让 LoadRunner 自动判定事务成功或失败,配合检查点才能把失败事务计入错误率。{username}、{pageNo}是参数占位符,值来自参数文件,下一节展开。事务名不要用中文里的特殊符号,避免 Analysis 里图表标签乱码。
4.2 参数化与关联:让脚本像真实用户,而不是复读机
参数化解决“所有用户用同一账号登录”的问题,关联解决“动态值写死导致回放失败”的问题。这两件事不做,压测数据基本作废。
参数化在脚本同级目录放一个users.dat,格式如下:
username,password,pageNo user01,pass01,1 user02,pass02,2 user03,pass03,3 user04,pass04,4 user05,pass05,5在 LoadRunner 里把参数类型设为“文件”,选择该 dat 文件,更新方式选“每次迭代”或“每次出现”,列分隔符设为逗号。用户数量与并发数匹配,比如并发 50 就准备至少 50 条账号,不够的话 LoadRunner 会循环取值,多个虚拟用户撞同一个账号,容易触发会话冲突,报出莫名其妙的错误。
关联针对的是服务端下发的动态值,最典型的是登录后的 sessionId 或表单里的隐藏 token。做法是在登录响应里用web.reg_save_param把值提取出来,后续请求用参数引用:
// 从登录响应中提取 sessionId,供后续请求使用 web.reg_save_param("sessionId", "NOTFOUND=warning", "LB=sessionId=", "RB=;", "Search=body", "ORD=1"); // 下单请求携带提取到的 sessionId web.url("提交订单", "http://localhost:8080/shop/order/submit;jsessionid={sessionId}", "TargetFrame=", "Resource=0", "RecContentType=text/html", "Referer=", web.LR_LAST);LB和RB是左右边界,Search=body表示在响应体里找,ORD=1取第一个匹配。NOTFOUND=warning比直接报错温和一些,但调试阶段建议先用NOTFOUND=error,强制暴露关联失败的问题,等脚本稳定了再放宽。我一般会先跑一次单用户回放,把日志级别开到最高,逐条确认每个动态值都取到了,再进场景压测,这一步省不得。
4.3 场景配置与执行:Controller 里的并发模型怎么设
脚本调通之后进 Controller 配场景。课程设计常用的模型是“手工场景 + 阶梯加压”:起始 10 个虚拟用户,每 30 秒增加 10 个,加到目标并发后持续跑 10 分钟,然后逐步释放。阶梯加压比一次性拉满更能看出系统在哪个并发点开始劣化,拐点数据在报告里非常好看。
几个配置细节容易被忽略。思考时间(Think Time)要保留,按录制时的比例回放或者手动设 2~5 秒,因为真实用户不会毫秒级连续点击,去掉思考时间等于把压力人为放大了数倍,结论会失真。集合点(Rendezvous)只在需要严格同步的场景用,比如测“同一时刻大量用户抢购”,普通负载测试不用加。日志级别在压测时降到“仅错误”,否则日志 IO 本身就会拖慢压测机。
执行完成后,Analysis 里重点导出四张图:平均响应时间随并发变化曲线、TPS 曲线、错误率曲线、资源监控(如果接了监控工具)。曲线要成组出现,单独一张响应时间图说明不了问题。
5. 避坑清单:性能测试课程设计里最容易翻车的五个点
这一章按“现象 → 原因 → 解决”写,都是实打实踩过的。
现象一:脚本单用户回放全部通过,一上并发就大面积报错。原因多半是参数化没做或数据不够,几十个虚拟用户循环用同几个账号,服务端会话互相顶掉;也可能是某个动态 token 被写死了,第一个用户用完之后失效。 解决:检查参数文件行数是否大于等于最大并发数,检查每个动态值是否都走了关联提取,回放日志里搜NOTFOUND确认为零。
现象二:响应时间曲线随并发数几乎线性上涨,TPS 上不去。先别急着下结论说系统性能差。大概率是压测机和被测服务在同一台机器上,CPU 被两边抢,或者压测机自身已经跑满。 解决:分开部署;压测过程中用系统监控看压测机 CPU,如果压测机先到 100%,这组数据作废,只能降并发重测。
现象三:数据库连接池报错,错误率突然飙升。连接池最大连接数设置过小,并发一高请求排队超时。这在 Javaweb + MySQL 的组合里太常见了。 解决:检查连接池配置(如最大连接数、等待超时),适当调大并重测;同时在报告里说明这是配置限制而非代码缺陷,避免结论写偏。
现象四:测试数据是空表或只有几条记录,查列表秒回,结论不可信。小数据量下 MySQL 走全表扫描也很快,测出来的响应时间没有参考价值。 解决:造数据,商品表至少几万条、订单表几万条,用脚本批量插入,造完再压。数据量要写进报告的环境说明。
现象五:报告里只有截图没有分析,导师一问就露馅。图贴了一堆,但没写“拐点出现在多少并发”“瓶颈在应用层还是数据库层”“优化建议是什么”。 解决:每张图后面跟一段解读,指出拐点、给出归因、提一条可执行的优化方向。数据分析部分才是性能测试的核心得分点。
注意:测试结论里不要写“系统性能良好”这种没有量化依据的话。写成“在 50 并发以内平均响应时间低于 1.5 秒、错误率为 0,超过 80 并发后响应时间快速上升,瓶颈初步定位在数据库连接池”,这才叫结论。
6. 验收与答辩:用一张结果对照表把整份课设讲圆
课设最后能不能过,很大程度取决于你有没有一张“设计目标 → 实测数据 → 是否达标”的对照表。答辩时间通常就十来分钟,导师不会听你把每个用例念一遍,他要看的是你有没有闭环意识。我习惯在报告最后一章放这么一张表,答辩时直接投屏讲:
| 验收项 | 设计目标 | 实测结果 | 结论 |
|---|---|---|---|
| 功能用例覆盖率 | 覆盖全部核心模块 | 4 个模块共 68 条用例 | 达标 |
| 用例执行通过率 | 不低于 90% | 93%(63/68) | 达标 |
| 缺陷修复验证 | 阻断级缺陷全部关闭 | 阻断 1 条、严重 3 条均已复测关闭 | 达标 |
| 负载场景响应时间 | 50 并发下平均低于 3 秒 | 1.42 秒 | 达标 |
| 负载场景错误率 | 低于 1% | 0.2% | 达标 |
| 压力场景表现 | 记录崩溃点与恢复情况 | 120 并发开始大量超时,降至 50 后恢复 | 已分析 |
讲的时候按这个顺序走:一句话说清被测系统和测试范围,然后讲用例设计方法(等价类、边界值各举一个例子就够),接着重点讲性能部分——为什么选这三个场景、并发数依据是什么、拐点在哪,最后落到这张对照表和优化建议。导师最容易追问的三个问题提前准备好答案:并发数怎么定的、瓶颈定位依据是什么、测试数据量多大。这三个答得出来,基本就稳了。
还有个细节容易被忽视:报告里的截图要有上下文,别只截一个“全部通过”的绿条。截用例执行结果时带上用例编号,截压测曲线时带上坐标轴标签和场景名称,让人一眼知道这张图对应哪个场景。截图命名也规范一下,跟用例编号或场景名对应,附录里一放,整份课设的完成度立刻不一样。
从那以后我每次带人做测试类课设,都强制走一遍“先定验收标准、再设计用例、最后拿数据回填对照表”的顺序,顺序反了就会陷入边测边编结论的被动局面。把标准写在前面,数据才有地方落。希望帮到你。
本文还有配套的精品资源,点击获取