news 2026/10/27 21:02:22

软件测试课程设计:JavaWeb可测系统与JUnit/LoadRunner实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
软件测试课程设计:JavaWeb可测系统与JUnit/LoadRunner实战

简介:面向高校计算机相关专业学生与软件测试初学者的课程设计实战包,以JavaWeb网上书店前台系统为被测对象,在《JavaWeb应用开发课程设计》基础上完成《软件测试课程设计》全部任务,解决测试用例设计无思路、工具上手难、报告缺素材的问题。压缩包共904个文件、约45.97MB,涵盖jsp页面、java源码、class编译产物、xml配置与jar依赖等工程文件,另有gif、jpg、png界面截图和docx说明文档,js、css、properties等前端与配置资源齐备,可直接用IDEA导入运行。内容覆盖白盒测试中的逻辑覆盖与基本路径覆盖用例设计、针对主要函数方法的单元测试,黑盒测试中的等价类划分与边界值分析,并用JUnit完成核心功能单元测试,辅以手工或自动化功能测试;性能测试部分借助LoadRunner开展稳定性与压力测试,通过逐步加压观察系统性能变化、确定最大服务级别。已有2350人学习,适合按模块对照练习、查漏补缺。

1. 软件测试课程设计:先有可测的系统,再谈用例和脚本

拿到软件测试课程设计这个题目,很多人的第一反应是打开 LoadRunner 录一段脚本,或者先翻书抄一页等价类划分表,结果卡在最前面:没有系统可测。拿一个只有登录框的 Demo 去写用例,写不满两页;直接搬一个开源商城,代码几十万行,连缺陷定位到哪个类都做不到。我一般建议反过来做:先用 JavaWeb + MySQL 搭一个四模块的小系统,业务闭环完整、代码量在可控范围,再用 IDEA 写 JUnit 单元测试覆盖白盒部分,用 LoadRunner 打并发覆盖性能部分。这套结构解决的是「测试对象、测试设计、测试执行、测试结论」四段证据链不完整的问题,适合要交一份完整课程设计报告、并且希望在答辩时能讲清每个数字来源的人。

2. 被测系统与环境:JavaWeb + MySQL 的四模块最小闭环

2.1 技术选型:可测性优先于功能丰富

被测系统的第一属性不是功能多,而是可测。三种常见选择摆在一起对比,结论很清楚。

方案代码量可定位性用例可写量对报告的价值
直接部署开源商城极大差,缺陷难定位到类多但写不完只能做黑盒,白盒章节空
纯前端静态 Demo极小无后端,无数据库少,逻辑单一性能测试没有意义
自写 JavaWeb 小系统约 2000 行好,Controller-Service-DAO 分层清晰刚好覆盖 40~60 条用例黑盒、白盒、性能三段都能落地

自写系统的代价是两三天工作量,收益是整份报告的每一章都有真实素材。Servlet + JSP 的经典结构足够,不必上 Spring 全家桶,因为层次越少,单元测试越好写:Service 层是纯 Java 方法,DAO 层只跟 JDBC 打交道,测试时可以把 DAO 换成内存实现,完全不依赖 MySQL 跑起来。

2.2 四个模块与四张表:把业务拆到能写用例的粒度

模块按「有权限差异、有状态流转、有输入校验」三条标准挑,四个就够:

模块核心功能对应的测试类型
用户模块注册、登录、改密、禁用输入校验、等价类、边界值、权限
商品模块增删改查、关键词搜索、分页组合条件、查询正确性
订单模块下单、取消、金额计算判定表、状态流转、事务一致性
权限模块普通用户 / 管理员路由拦截越权访问、会话失效

数据表不用多,四张表撑起全部流程。建表时把「可观测点」提前埋进去,后面写用例会省很多事。

CREATE TABLE `user` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', `username` VARCHAR(32) NOT NULL COMMENT '登录名,业务规定3-16位', `password` CHAR(32) NOT NULL COMMENT 'MD5摘要,固定32位', `phone` VARCHAR(11) DEFAULT NULL COMMENT '手机号,11位', `status` TINYINT NOT NULL DEFAULT 1 COMMENT '1正常 0禁用,供禁用登录用例使用', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) -- 唯一索引是“重复注册”用例的判定依据 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; CREATE TABLE `orders` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `user_id` INT UNSIGNED NOT NULL, `total_amount` DECIMAL(10,2) NOT NULL COMMENT '订单总额,保留两位小数', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0待支付 1已支付 2已取消', PRIMARY KEY (`id`), KEY `idx_user` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';

字段长度不是随手写的:username VARCHAR(32)但业务规定 3~16 位,于是 2、3、16、17 这四个值天然成为边界值用例的数据;status字段让「禁用用户登录」这条用例有了可操作入口,不用去改代码;uk_username唯一索引让「重复注册」的预期结果从「提示错误」变成「数据库抛 Duplicate entry」,可以设计成两条不同层级的用例,一条黑盒看提示语,一条看事务回滚。total_amount用 DECIMAL 而不是 DOUBLE,是为了在金额精度用例里能稳定复现 0.1+0.2 的问题,浮点类型会让这条用例时对时错,报告里不好解释。

2.3 环境搭建与版本对齐:四个组件的兼容矩阵

环境不一致是课程设计里最高频的翻车点。下面这套组合实测稳定,装机顺序也按这个走。

组件建议版本作用常见冲突
JDK11编译运行,java.net.http接口测试要用用 8 会缺 HttpClient
Tomcat9.xServlet 4.0 容器Tomcat 10 起包名变jakarta.*,老代码全红
MySQL8.0数据存储5.7 与 8.0 驱动类名不同
IntelliJ IDEA2021 以上开发与单元测试社区版不支持部分 Web 插件
Maven3.8依赖管理镜像未配置时依赖下载卡住
LoadRunner12.5x 或社区版性能测试社区版限 50 虚拟用户,课程设计够用

Maven 依赖里最容易错的是作用域,写错了会出现「本地跑得好好的,打包到 Tomcat 就报类冲突」。

<dependencies> <!-- Servlet API 由 Tomcat 提供,scope=provided 才不会打进 war 包 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!-- MySQL 8 的驱动,类名是 com.mysql.cj.jdbc.Driver --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <!-- 单元测试依赖,scope=test 保证不进运行时 --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.9.2</version> <scope>test</scope> </dependency> </dependencies>

工程目录按分层建,测试代码和业务代码同包名放在src/test/java下,IDEA 才能直接识别。

shop/ ├── src/main/java/com/demo/shop/ │ ├── controller/ # Servlet,接收请求 │ ├── service/ # 业务逻辑,单元测试的主战场 │ ├── dao/ # JDBC 访问,测试时用内存实现替换 │ └── entity/ # 实体类 ├── src/main/resources/ │ └── db.properties # 数据库连接配置 └── src/test/java/com/demo/shop/service/ └── UserServiceTest.java

db.properties里的连接串必须带三个参数,缺一个就会在启动时报时区或编码错误:useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai。这一步做完,先手工点一遍注册登录,确认数据库里有数据落进去,再开始写用例——系统本身跑不通就去设计用例,后面所有执行记录都是假的。

3. 用例设计体系:等价类、边界值与判定表的三层落地

3.1 需求条目化:把功能点拆成可追溯的测试项

直接从「用户模块」写用例,写到第十条就会开始重复。我的做法是先做一张需求条目表,每个条目一个编号,后面所有用例都挂在这个编号下,报告的覆盖矩阵才能自动生成。

需求编号功能点输入约束优先级
R-01用户注册用户名、密码、手机号用户名 3-16 位且唯一P0
R-02用户登录用户名、密码账号状态为正常P0
R-03商品搜索关键词、页码每页 10 条P1
R-04订单金额计算商品单价、数量、会员状态满 99 免运费P0
R-05越权访问用户 ID、目标 URL普通用户不能进后台P0

条目化之后再看,R-04 这种「多个条件共同决定一个结果」的功能,等价类划分就不够用了,必须上判定表;R-01 这种「单个输入有明确范围」的,等价类加边界值最快。选错方法会让用例数量和缺陷发现率对不上,答辩时被问「为什么这个功能只写了三条用例」就很难答。

3.2 等价类与边界值:以用户名为例的完整推导

用户名规则「3~16 位,只允许字母数字下划线,不可重复」,先划分等价类,再取边界。

类编号类型数据范围代表值
E1有效等价类3~16 位合法字符tom_01
E2无效等价类长度 < 3ab
E3无效等价类长度 > 1617 位字符串
E4无效等价类含非法字符tom@01
E5无效等价类为空空串
E6无效等价类已存在tom_01重复提交

边界值只取 E1/E2/E3 的交界:2、3、16、17 四个点,其中 3 和 16 是有效边界,2 和 17 是无效边界。落地成用例表:

用例编号对应需求输入预期结果优先级
TC-01-01R-01tom_01注册成功,跳转登录页P0
TC-01-02R-01ab提示「用户名长度 3-16 位」P0
TC-01-03R-0116 位字符串注册成功P1
TC-01-04R-0117 位字符串提示长度超限P0
TC-01-05R-01tom@01提示含非法字符P1
TC-01-06R-01空串提示不能为空P1
TC-01-07R-01重复tom_01提示用户名已存在P0

注意 TC-01-03 和 TC-01-04 这一对,很多人只写 17 位不写 16 位,结果边界上真有一个 off-by-one 的缺陷也发现不了。边界值的关键是「两侧都取」,不是只取越界那一侧。

3.3 判定表:订单运费这种多条件组合的正确写法

R-04 的规则是:订单满 99 元免运费;不满 99 元时会员收 5 元、非会员收 10 元。两个条件、四种组合,直接画判定表。

条件 / 动作规则 1规则 2规则 3规则 4
订单金额 ≥ 99是是否否
是否会员是否是否
运费00510
对应用例TC-04-01TC-04-02TC-04-03TC-04-04

四条规则对应四条用例,一条不多一条不少。规则 1 和规则 2 的运费相同,看起来可以合并,但不能合并——因为「金额满 99 时会员身份是否还被读取」这件事本身就是一个可测点,如果代码里先判断会员再判断金额,规则 2 可能返回 5 而不是 0,这个缺陷只有拆开写才会暴露。合并用例是判定表使用中最常见的错误,合并的依据是「动作相同」,但测试要验证的是「条件组合的处理路径」,不是最终数值。

金额边界还要再补两条:98.99 和 99.00。DECIMAL 类型下这两个值的比较是精确的,换成 DOUBLE 就会出现 99.00 被判成 98.999999 的玄学问题,这也是我在建表时坚持用 DECIMAL 的原因。

3.4 白盒覆盖:语句、判定、条件覆盖的取舍

报告里的白盒章节不能只有一张流程图。拿订单运费这段代码做例子,四种覆盖标准的差距一目了然。

public static int calcFreight(double amount, boolean isMember) { if (amount >= 99) { // 判定 D1 return 0; // 满 99 免运费 } if (isMember) { // 判定 D2 return 5; // 会员半价运费 } return 10; // 普通用户全额运费 }
  • 语句覆盖:走(50, true)和(100, false)两条,能执行到全部三条 return,但 D1 为真的分支没测到运费为 10 的情况。
  • 判定覆盖:D1 真假各一次、D2 真假各一次,需要(100, *)、(50, true)、(50, false)三条。
  • 条件覆盖:本例中每个判定只有一个条件,条件覆盖与判定覆盖等价,这正是很多人写混的地方——条件覆盖只有在判定内含&&或||时才与判定覆盖有区别。
  • 路径覆盖:三条独立路径D1真、D1假→D2真、D1假→D2假,三条用例全覆盖。

课程设计一般要求做到判定覆盖,把这三条用例写进 JUnit 参数化测试,跑出来的绿条截图就是白盒章节最硬的证据。想做条件覆盖,就把代码改成if (amount >= 99 && isMember)这种复合判定,再补(100, true)、(100, false)两条,报告里能多出「条件组合」一节,说服力更强。

3.5 用例表字段与优先级划分

用例表字段固定下来,后面执行和统计才不会乱。

字段说明
用例编号模块-功能-序号,如 TC-01-03
前置条件数据库状态、登录状态、已有数据
操作步骤编号步骤,能照着点
测试数据具体值,不写「输入合法用户名」
预期结果可判定的描述,含提示语原文
优先级P0 冒烟、P1 主流程、P2 异常分支
执行结果通过 / 失败 / 阻塞

优先级不是按功能重要性拍的,按「失败后能不能继续测」排:登录失败,后面全阻塞,所以 P0;改密页面的输入校验失败,不影响主流程,P2。冒烟用例控制在 10 条以内,答辩现场先跑这一组,十秒出结果,比讲十分钟设计思路管用。

4. 用例执行:JUnit 单元测试与 LoadRunner 脚本的工程化落地

4.1 JUnit 5 单元测试:让白盒用例可重复执行

手工点页面只能证明「这次是对的」,证明不了「每次都对」。Service 层用 JUnit 5 写成参数化测试,一条用例一个方法,边界值一张表跑完。

class UserServiceTest { private UserService userService; @BeforeEach void setUp() { // 用内存实现替换真实 DAO,单元测试不依赖 MySQL,跑一次 2 秒 userService = new UserService(new InMemoryUserDao()); } @ParameterizedTest(name = "用户名[{0}] 期望结果={1}") @CsvSource({ "tom_01, true", // 有效等价类 "ab, false", // 边界:长度 2 "abc, true", // 边界:长度 3 "1234567890123456, true", // 边界:长度 16 "12345678901234567, false", // 边界:长度 17 "tom@01, false", // 非法字符 "'', false" // 空串 }) @DisplayName("TC-01 系列:用户名格式校验") void checkUsername(String name, boolean expected) { assertEquals(expected, userService.isValidUsername(name)); } @Test @DisplayName("TC-01-07:重复用户名应抛出业务异常") void registerDuplicate() { userService.register("tom_01", "123456", "13800000000"); // 断言抛出的异常类型,而不是断言返回 false,避免实现改动后用例失效 BusinessException ex = assertThrows(BusinessException.class, () -> userService.register("tom_01", "123456", "13800000000")); assertEquals("用户名已存在", ex.getMessage()); } }

@ParameterizedTest配合@CsvSource的好处是,一次执行产生 7 条独立结果,IDEA 的测试报告里会逐条列出「用户名[ab] 期望结果=false」,截图放进报告就是一页完整的边界值执行记录。@DisplayName里直接写用例编号,测试结果和用例表能一一对应,答辩时打开测试面板,谁都能看出覆盖关系。

assertThrows那个细节值得多说一句。很多写法是断言注册方法返回false,但真实业务里重复注册通常是抛异常回滚事务,返回false的实现在并发下会出现脏数据。断言异常类型而不是布尔值,用例才对实现有约束力。

DAO 层想测的话,用 H2 内存库加MODE=MySQL兼容模式,@BeforeEach里建表、@AfterEach里清表,能验证 SQL 是否写错。不过课程设计时间紧,DAO 层的验证用真实 MySQL 手工跑一遍 SQL 也能交差,把精力集中在 Service 层的逻辑覆盖上更划算。

4.2 接口测试:绕过页面直接打 HTTP

单元测试覆盖了逻辑,页面到 Servlet 这一段还没验证。用 JDK 11 自带的java.net.http发请求,不引第三方库。

class LoginApiTest { private final HttpClient client = HttpClient.newHttpClient(); private static final String BASE = "http://127.0.0.1:8080/shop"; @Test @DisplayName("TC-02-03:密码错误应返回 401 且提示文案正确") void loginWithWrongPassword() throws Exception { String body = "username=tom_01&password=wrong"; HttpRequest req = HttpRequest.newBuilder() .uri(URI.create(BASE + "/login")) .header("Content-Type", "application/x-www-form-urlencoded") .POST(HttpRequest.BodyPublishers.ofString(body)) .build(); HttpResponse<String> resp = client.send(req, HttpResponse.BodyHandlers.ofString()); assertEquals(401, resp.statusCode()); // 状态码断言 assertTrue(resp.body().contains("用户名或密码错误")); // 文案断言 } }

状态码和文案两条断言都要有。只断状态码,前后端约定改了用例照样绿;只断文案,500 错误页里也可能碰巧包含这几个字。跑这组测试前要先启动 Tomcat,所以它属于集成测试,放在src/test下单独建一个包,用 Maven 的failsafe插件和单元测试分开执行,或者干脆在 IDEA 里手工点运行——课程设计阶段没必要上 CI。

4.3 LoadRunner 脚本:录制、参数化与事务边界

性能部分用 LoadRunner 的 VuGen 录脚本,协议选 Web - HTTP/HTML。录制模式建议 URL-based,HTML-based 会把页面里的图片、CSS 一起录进去,脚本里多出几十条无关请求,事务时间统计会失真。

录完先做三件事:删冗余请求、加事务、加检查点。登录这个场景改造完大概是这样。

Action() { /* 1. 事务开始:只有登录请求计入响应时间 */ lr_start_transaction("login"); /* 2. 检查点必须放在请求之前注册,否则匹配不到响应内容 */ web_reg_find("Text=欢迎", "SaveCount=welcome_cnt", LAST); /* 3. 用户名参数化,密码固定,避免所有虚拟用户撞同一个账号 */ web_submit_data("login", "Action=http://127.0.0.1:8080/shop/login", "Method=POST", "RecContentType=text/html", ITEMDATA, "Name=username", "Value={p_username}", ENDITEM, "Name=password", "Value=123456", ENDITEM, LAST); /* 4. 事务结束,LR_AUTO 根据检查点自动判定成功或失败 */ lr_end_transaction("login", LR_AUTO); /* 5. 思考时间放在事务外,否则响应时间会被拉长 */ lr_think_time(3); return 0; }

五个位置都是踩过坑才记住的。web_reg_find是注册型函数,写在web_submit_data后面就永远匹配 0 次,事务全判失败;lr_think_time放在事务内,测出来的「响应时间」其实是用户看页面的时间,能到三四秒,答辩时被问「服务器为什么这么慢」就说不清了;LR_AUTO依赖检查点的SaveCount判断事务状态,检查点没注册成功,事务就永远是成功的,错误率恒等于 0,这份性能报告等于没测。

参数化配置在 Parameter List 里完成:新建一个login.dat文件,一行一个用户名,Select next row 选Unique,Update value on 选Each iteration。数据行数要大于等于虚拟用户数乘以迭代次数,否则 LR 会报「No more unique values」,跑到一半掉用户,TPS 曲线会出现断崖。

4.4 场景设计与结果分析:三个必须写进报告的指标

Controller 里建手动场景,虚拟用户数按 10、30、50 分三档阶梯加压,每档持续 5 分钟,加压方式选Ramp up,每 15 秒起 2 个用户。一次只改一个变量,否则说不清性能变化是并发数引起的还是数据量引起的。

Analysis 里重点看三个图,不需要全看:

指标位置关注点报告里怎么写
平均事务响应时间Average Transaction Response Time随并发上升的拐点50 并发时 login 事务 P90 为 1.8s
TPSTransactions per Second是否出现平台期或下降30 并发后 TPS 不再增长,判定存在瓶颈
错误率场景状态页是否超过 1%50 并发时错误率 6%,原因为连接池耗尽

写结论时用「90% 响应时间」而不是平均值。平均值会被少数极慢的请求掩盖,一次 500 并发测试里平均值 0.8 秒可能对应 90% 用户 2.5 秒,把两个数并排放,报告的可信度立刻不一样。发现瓶颈后要补一条原因分析,比如连接池最大连接数默认 10,50 并发下大量请求排队等待连接,把maxPoolSize调到 50 再测一轮,两轮数据对比就是一份完整的性能优化记录。

5. 避坑与排查:环境、脚本、报告三类翻车点的定位方法

5.1 环境类:驱动、端口与乱码

现象:IDEA 里跑单元测试报ClassNotFoundException: com.mysql.cj.jdbc.Driver,或者No suitable driver found。

原因:MySQL 5.x 的驱动类是com.mysql.jdbc.Driver,8.x 改成了com.mysql.cj.jdbc.Driver,配置文件里的类名和 pom 里的版本对不上;另一种情况是mysql-connector-java的 scope 被写成了provided,测试时根本不在类路径里。

解决:先把db.properties的驱动类名和 pom 版本对齐到 8.0.x,再检查 scope 是不是默认的 compile。URL 里补serverTimezone=Asia/Shanghai,否则 8.0 驱动会报时区未识别。

现象:Tomcat 启动报Address already in use: 8080,或者页面中文全变问号。

原因:上一次启动的 Tomcat 进程没退干净;乱码一般是请求体和响应体编码不一致,POST 请求没设request.setCharacterEncoding("UTF-8"),GET 请求在 Tomcat 8 之后默认已经是 UTF-8,只有 POST 需要手工设。

解决:netstat -ano | findstr 8080找到 PID 后结束进程;编码在 Servlet 最前面统一设一次,响应头加response.setContentType("text/html;charset=UTF-8"),两个都加才是完整的。

5.2 脚本类:空脚本、参数失效与事务时间异常

现象:LoadRunner 录制结束,脚本里只有Action()的空壳,一条请求都没有。

原因:录制时没有使用本机回环地址,或者浏览器的代理设置被其他工具占用;另一种常见情况是协议选错,JavaWeb 应用如果用了 AJAX 提交,只选 Web - HTTP/HTML 有时抓不到异步请求。

解决:先用浏览器直接访问被测地址确认服务通,再检查 VuGen 的 Recording Options 里代理端口有没有冲突。实在录不上就手写请求,用web_custom_request拼一条最简单的 GET,能返回 200 说明链路通了,再往上加参数。手写脚本比反复录制更快,也更适合当成报告里的「脚本设计」章节。

现象:参数化之后,所有虚拟用户都用同一个用户名,登录后互相把对方的会话挤掉,错误率飙升。

原因:Parameter List 里 Update value on 选成了Once,或者 Select next row 用了Sequential但数据行数少于总迭代次数,LR 循环回第一行复用。

解决:账号参数统一设成Unique + Each iteration,并勾选「When out of values: Continue in a cyclic manner」。做完在 Controller 里跑 5 个用户验证,看 Analysis 的日志确认每个用户取到了不同值。

现象:事务平均响应时间 3 秒以上,但手工点页面感觉很快。

原因:lr_think_time写在了lr_start_transaction和lr_end_transaction之间,思考时间被计入了事务。

解决:事务只包裹请求本身,思考时间、集合点、日志输出全部放事务外。改完重跑一轮,两个数据都保留在报告里,正好用来解释「为什么事务响应时间和用户体验时间要分开统计」。

5.3 报告类:缺陷复现不了与结论没有依据

现象:提交的缺陷,开发照着步骤点了一遍说「没问题」,用例被判为无效。

原因:缺陷单只写了「搜索功能有问题」,没写关键词、页码、当前数据量、浏览器和账号角色。搜索类缺陷极度依赖数据集,10 条商品和 1000 条商品的结果完全不同。

解决:缺陷单固定六个字段——环境(含数据库数据量)、前置条件、操作步骤、实际结果、预期结果、复现概率。复现概率这一栏逼着人跑三遍再提交,能过滤掉一半的偶发误报。

现象:性能测试报告只有一张折线图,被问「这个结果算好还是不好」时答不上来。

原因:没有基线。脱离基线的响应时间只是一个数字。

解决:在报告里加一列「判定标准」,比如「登录 P90 < 2s 为合格、2~3s 为可接受、> 3s 为不合格」,这个标准由需求或历史数据推出来,写在测试计划里,执行完直接对照。有基线的报告才叫测试结论,没基线的只是数据记录。

6. 验收与答辩:三张表把课程设计讲成一套可交付方案

答辩现场最容易失分的不是做得多,而是讲不清。我一般要求把材料压缩成三张表,讲的时候按表走,五分钟能说完整套逻辑。

表名内容回答什么问题
需求-用例覆盖矩阵需求编号 × 用例编号 × 数量,标出未覆盖项测试是否充分
用例执行统计总数、通过、失败、阻塞,按模块和优先级分组系统质量如何
性能指标对比并发档位 × TPS × P90 × 错误率,含优化前后两轮瓶颈在哪,怎么定位

第一张表用来挡「你测全了吗」。覆盖矩阵里留一两项明确的未覆盖(比如「支付回调依赖第三方,本次不测」),比声称 100% 覆盖更可信;评审老师一问就能看出你想过边界。第二张表用来挡「发现了几个缺陷」,通过率不用造得很漂亮,85% 左右的通过率加三条有完整复现步骤的缺陷记录,比 100% 通过更有说服力。第三张表用来挡「性能瓶颈在哪」,两轮数据放一起,把连接池参数改动写进备注,因果链就完整了。

演示顺序也有讲究。别一上来就打开报告念目录,先跑 JUnit——IDEA 里点一下,十秒出绿条,参数化用例逐条列出来,白盒覆盖当场就证明了;接着在 Controller 里跑一个 10 用户、1 分钟的小场景,让评审看到 TPS 曲线实时往上走;最后再翻报告,这时候每一页图表都有前面的现场演示兜底,讲起来底气完全不一样。整套命令我一般提前写成脚本,现场不手敲,一个字母打错就要重来,血泪经验。

必答的三个问题提前准备好答案:为什么用等价类而不是判定表(单输入有明确范围,多条件组合才上判定表);性能瓶颈的定位依据是什么(响应时间随并发上升但 TPS 不涨,同时观察到连接池等待队列变长);缺陷怎么保证不是环境问题(缺陷单里有环境快照和复现概率)。答的时候给数据和位置,别给结论。

从那以后我每次带人做这类课程设计,都强制走一遍「先跑一遍冒烟用例、再翻报告」的顺序,把现场演示当成验收的一部分,而不是答辩前一晚才补的表演。系统跑得起来、用例执行得出来、数据说得清来源,这三件事做到,一份课程设计就站得住。配套的工程源码、用例表模板、LoadRunner 脚本和报告框架我已经按上面的结构整理成一个压缩包,改掉包名和数据库连接就能直接跑,希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/27 21:00:45

YOLOv5锥桶识别全流程:数据标注、训练调优与TensorRT部署

1. 锥桶识别这件事&#xff0c;难在哪儿第一次接锥桶识别需求的时候&#xff0c;我以为这是个送分题。橙色、圆锥形、纹理单一、背景通常是路面&#xff0c;按直觉应该比行人车辆好检。真跑起来才发现&#xff0c;这个目标物几乎集合了所有让检测模型难受的特性&#xff1a;远距…

作者头像 李华
网站建设 2026/10/27 20:59:49

营销技能的本质是可验证的商业反应系统

1. “marketingskills”不是技能清单&#xff0c;而是一套可验证的商业反应系统你搜“marketingskills”&#xff0c;页面弹出的全是“20个必备营销技能”“Top 10 Marketing Skills for 2024”这类标题——点进去一看&#xff0c;基本是“沟通能力”“数据分析”“内容创作”“…

作者头像 李华
网站建设 2026/10/27 20:59:45

K8S持久化存储:PV/PVC、StorageClass与CSI实践

容器里跑着跑着&#xff0c;Pod 被驱逐一次、节点宕机一次&#xff0c;或者只是简单地滚动更新了一版镜像&#xff0c;日志里就冒出一句no such file or directory——数据没了。这个场景在 K8S 持久化存储这个话题下几乎天天上演&#xff0c;也是我从单体应用迁到 K8S 之后踩得…

作者头像 李华
网站建设 2026/10/27 20:59:29

编程操作符全解析:从基础到高阶应用

1. 操作符深度解析&#xff1a;从基础到高阶的完整指南在编程世界里&#xff0c;操作符就像数学中的加减乘除符号&#xff0c;但功能远不止于此。作为代码中最基础的构建块&#xff0c;操作符直接决定了程序的行为逻辑和数据流向。很多初学者往往低估了操作符的重要性&#xff…

作者头像 李华
网站建设 2026/10/27 20:58:40

e2e测试的本质:可控现实模拟器与工程临界点

1. “e2e”不是缩写&#xff0c;是工程实践的临界点很多人第一次看到“e2e”三个字母&#xff0c;下意识会去查全称——End-to-End&#xff1f;没错&#xff0c;但这个答案只开了个门缝。真正让“e2e”在工程师日常中高频出现、反复调试、深夜报错、团队争论的&#xff0c;从来…

作者头像 李华
网站建设 2026/10/27 20:58:16

C# WinForms学生信息管理系统(含SQL Server完整源码)

简介&#xff1a;本资源是一套面向编程初学者与教育信息化实践者的《学生信息管理系统》完整开发包&#xff0c;聚焦小程序级数据库应用开发&#xff0c;适用于高校计算机基础课程设计、教务管理工具原型开发及PythonSQLite入门实战。压缩包共4个文件&#xff0c;含2份Word文档…

作者头像 李华