简介:面向软件技术、软件测试专业学生与初学者,这份测试报告以博客系统为被测对象,完整覆盖软件测试从需求分析、用例设计到执行、缺陷分析的规范流程。报告围绕个人博客空间、个人博客管理和博客后台管理三大模块展开,运用单元测试、集成测试及黑盒测试技术,给出登录模块、信息模块和首页显示等功能的详细测试用例,并以预期输出与实际结果对照方式记录问题,便于理解软件测试的实操要点。压缩包仅1个文件,为PDF格式,大小约589KB,内容附有系统功能结构图和测试流程图,可直接作为《软件测试》课程实训案例、毕业设计测试报告模板或软件测试入门参考。目前已有117人学习下载,适合需要系统了解博客类系统测试流程、撰写测试文档的高校学生与初级测试人员。
1. 一份 2011 年的博客系统测试报告,现在读还能捞出什么
翻开源文件,配置项写着 AMD4400+、1G 内存、160G 硬盘、Windows 2000/XP,测试时间停在 2011 年 11 月底。这份软件测试报告对应的对象是一个典型的个人博客系统,核心功能是共享文章、图片和心得,让用户通过注册登录实现留言、加好友、浏览相册等互动。对我这种经常做测试评审的人来说,旧项目的价值不在于结论,而在于测试用例的写法和缺陷观察的粒度。报告里能看出三个清晰的模块边界:个人博客空间、个人博客管理、博客后台管理,并且用单元测试加集成测试的组合把每个入口都过了一遍。适合两类人:一类是刚接手测试报告编写、需要参考用例结构和测试表格的初级测试工程师;另一类是做系统重构、需要从历史测试数据反推业务链路的技术人员。
2. 单元测试与黑盒用例分解:三个功能模块的测试策略选型
2.1 为什么这个项目选单元测试打底
测试策略和技术段落里,博客系统被拆成了个人博客空间、个人博客管理、博客后台管理三大模块。报告的思路是以单元测试为主,等模块各自跑通以后再做集成测试。这个选型对当时的项目规模是合理的:三个模块之间虽然共享同一份用户数据,但职责边界很清晰。前台浏览不依赖后台管理,权限管理只作用于管理员入口,模块之间有足够的独立性,可以单独拉起页面做观测。
对测试人员来说,这里的单元测试并不是通常意义上工程师写的代码级测试,而是模块级黑盒测试。测试人员从用户视角操作页面,验证每个功能的输入输出是否符合预期。报告里特意提到“从用户的角度,通过测试可以更好的观察到每个模块的运行状态”,说明当时的测试重点是功能正确性,不是代码覆盖率。
下面这张表对比了本项目三种可用的测试策略选型,方便理解为什么单元测试放在第一位。
| 测试策略 | 测试对象 | 执行时机 | 关注点 | 在本项目中的体现 |
|---|---|---|---|---|
| 单元测试 | 单个程序模块 | 模块开发完成后 | 模块内部逻辑与输入输出 | 依次验证首页、登录、信息模块 |
| 集成测试 | 模块间连接关系 | 所有模块通过单元测试后 | 接口契约、参数传递、数据一致性 | 从首页登录后进入各功能区 |
| 系统测试 | 完整系统 | 集成测试通过后 | 整体流程、兼容性、性能 | 报告中未展开,可后续补充 |
单元测试是集成测试的前置条件,但不代表单元测试全过、集成就能通。报告后半部分记录的 500 错误和页面空白,恰恰发生在模块连接阶段。这说明模块单独跑和模块连起来跑,是两种完全不同的问题域。
2.2 黑盒测试用例骨架怎么写
报告里的测试没有写自动化脚本,但用例的组织方式可以直接落成可重复执行的代码。我一般会把报告里手工点击的路径固化成一个黑盒用例骨架,每个用例只做一件事:打开页面,观察结果,和预期比对。
# blog_smoke_test.py # 黑盒用例骨架:把报告里的链接点击行为转成断言 def test_homepage_navigation(): """验证首页各功能区链接是否能正确落地""" cases = [ {"name": "功能导航区", "path": "/blog/index.jsp", "expected": 200}, {"name": "推荐博客区", "path": "/blog/recommend.jsp", "expected": 200}, {"name": "推荐阅读区", "path": "/blog/essay.jsp", "expected": 200}, {"name": "热门博客区", "path": "/blog/hot_blog.jsp", "expected": 200}, ] for case in cases: status = open_page(case["path"]) # 模拟真实用户请求 assert status == case["expected"], ( f"{case['name']} 期望 {case['expected']},实际 {status}" )这个骨架的价值是把报告里的功能点清单固化成可重复的断言,而不是靠手工一遍遍点。case 里的 name 对应报告里的首页显示模块、功能导航区这类测试单元;path 是页面入口,在当年 JSP + Servlet 环境下一般是 xxx.jsp 或 servlet 路由;expected 设的是正常状态码。实际执行时如果返回 500,断言会直接告诉你具体是哪个区域出了问题。
2.3 三个模块的功能结构拆解
个人博客空间面向所有访客,匿名状态下可以浏览文章、浏览相册、访问好友博客;登录后额外获得发表留言、添加好友、维护个人资料的权限。个人博客管理面向注册用户,负责管理自己的资料、文章和相册。博客后台管理面向管理员,包含用户管理、文章管理、相册管理和修改管理员密码。
个人博客前台和管理员后台是两条独立的入口链路。普通用户登录走前台登录框,管理员登录走后台登录页面,两条链路对身份的校验逻辑不同。这也是集成测试时最容易出问题的地方:session 里存的用户身份标识是否区分了普通用户和管理员,直接决定你能否访问后台管理页面。很多实训项目会在这一层埋权限缺陷,比如普通用户修改 URL 参数后能直接操作管理员功能。
3. 登录校验与首页展示的用例设计:等价类划分和状态断言
3.1 从测试报告还原登录校验规则
报告登录模块记录了一组很典型的输入校验规则:用户名和密码不能为空,用户名必须以英文、下划线或美元符号开头,用户名长度必须大于 6 个字符,密码长度必须大于 6 个字符。这套规则放到现在的 Web 系统里依然成立,而且非常适合用等价类划分来设计用例。
等价类划分的思路是把输入域分成有效等价类和无效等价类,每个等价类选一个代表值测试。用户名规则至少可以划分出七类:空值、非法首字符、合法首字符但长度不足、合法长度但含非法字符、合法边界值 7 位、超长非法值、完全合法的组合。下面这张表按报告里的记录做了还原。
| 测试项 | 输入 | 预期输出 | 实际结果 | 对应等价类 |
|---|---|---|---|---|
| 用户名为空 | 空 + 合法密码 | 用户不可以为空 | 提示正确 | 无效-空值 |
| 密码为空 | 合法用户名 + 空 | 密码不可以为空 | 提示正确 | 无效-空值 |
| 非法首字符 | Xyx22 + 合法密码 | 用户名必须以英文、_、$ 符号开头 | 提示正确 | 无效-首字符 |
| 长度不足 | Xyx22 + 合法密码 | 用户名长度必须大于 6 个字符 | 提示正确 | 无效-边界 |
| 密码长度不足 | 合法用户名 + 2xyx4 | 密码必须大于 6 个字符 | 提示正确 | 无效-边界 |
| 正确登录 | Xyx2ss + Xyx2ss | 登录成功 | 登录成功 | 有效等价类 |
| 匿名登录 | 不填写 | 匿名登录成功 | 登录成功 | 有效等价类 |
注意用户名规则里有个细节:首字符校验和长度校验是两条独立规则,测试时不能合并。报告里 Xyx22 这个输入同时触发了首字符和长度两条提示,说明系统是依次校验的。设计用例时要把这种组合拆开,否则无法定位是哪条规则先拦截了请求。这类博客系统的用户模块在头歌等实训平台上经常作为考核点,登录校验的用例设计是否完整直接影响评分。
3.2 用 requests 把登录流程脚本化
手工点击只能验证一次,脚本化之后可以反复回归。用 requests 模拟表单提交,关键是把 session 保持住,否则后续请求不会携带 Cookie,登录状态会丢失。
# login_test.py import requests BASE_URL = "http://localhost:8080/blog" def test_login_success(): """验证合法用户名密码可以正常登录""" session = requests.Session() resp = session.post( f"{BASE_URL}/login", data={"username": "Xyx2ss", "password": "Xyx2ss"}, allow_redirects=True, ) # 登录成功后应跳转到个人主页,而不是回到登录页 assert resp.status_code == 200 assert "个人主页" in resp.text, "登录成功后未进入个人空间"这里用 Session 对象保存 Cookie,模拟的是浏览器在同一次会话里的连续请求。data 参数对应表单里的 username 和 password 字段,allow_redirects 设置为 True 是为了跟随登录成功后的页面跳转。断言只检查两件事:状态码是 200,页面里出现个人主页的标志性文本。如果登录失败被重定向回登录页,后面那条断言会立即失败。
3.3 首页展示模块的预期输出与实际结果
报告对首页面显示模块做了六个子项的测试,覆盖功能导航区、推荐博客区、推荐阅读区、热门博客区、热门文章区、主页面信息。预期输出都是链接页面正常显示,实际结果却不理想:推荐博客、推荐阅读、热门博客、热门文章四个区域全部出现 500,主页面信息出现空白。
| 功能区域 | 预期输出 | 实际结果 | 备注 |
|---|---|---|---|
| 功能导航区 | 链接页面显示 | 链接页面显示 | 正常 |
| 推荐博客区 | 链接页面显示 | 500 | 服务端异常 |
| 推荐阅读区 | 链接页面显示 | 500 | 服务端异常 |
| 热门博客区 | 链接页面显示 | 500 | 服务端异常 |
| 热门文章区 | 链接页面显示 | 空白 | 响应无内容 |
| 主页面信息 | 首页面显示 | 空白 | 响应无内容 |
首页导航区正常但热门区域大面积 500,这个现象很有指向性。导航区是纯静态链接,不应该出问题;热门区域需要查询数据库并按热度排序,500 大概率是数据查询环节抛了异常,比如 SQL 语法错误、数据表不存在,或者查询结果集在 JSP 页面里被错误地循环引用。空白页则是另一种情况,HTTP 状态码可能还是 200,但响应体没有内容,常见原因是 JSP 编译失败或页面转发时丢失了响应数据。这两种现象的排查路径完全不同,后面集成测试章节会展开。
4. 信息模块与后台管理:从用例表到 URL 级链路校验
4.1 信息模块的四个入口和测试数据
信息模块包含我的文章、我的相册、我的资料、我的好友四个入口,报告里这四个入口全部命中 500。这种全量失败的情况,优先怀疑的不是四个功能各自的业务逻辑,而是它们共同依赖的东西,比如用户会话、统一布局页面、数据库连接。
准备测试数据时要尽量模拟真实用户。博客系统的数据库设计通常包含用户表、文章表、评论表、相册表四张核心表,测试数据需要保证外键关系完整。下面这组 SQL 构造了一个带文章、相册和好友记录的用户,专门用于链接验证。
-- 准备测试数据:一个拥有完整业务记录的用户 INSERT INTO blog_user (uid, username, password) VALUES (1001, 'test_blogger', 'Xyx2ss'); -- 文章表关联用户 uid,用于个人博客管理模块 INSERT INTO blog_article (uid, title, content) VALUES (1001, '测试文章', '用于链接有效性验证的内容'); -- 相册表同样关联 uid,内容字段可留空 INSERT INTO blog_album (uid, album_name) VALUES (1001, '测试相册'); -- 好友关系表记录两两关系,friend_uid 指向另一个用户 INSERT INTO blog_friend (uid, friend_uid) VALUES (1001, 1002);执行测试前先确认四张表的数据落库成功,再进入页面观察。如果数据库里没有任何文章和相册记录,页面链接即使正常也可能显示空列表,会把“页面缺陷”和“数据缺失”混在一起,干扰缺陷定位。
4.2 URL 级链接探测,替代手工点击
四个子功能全部 500,手工点击效率太低。更快的做法是直接把链接清单拉出来做批量探测,通过状态码分布缩小排查范围。下面这段脚本会遍历报告里提到的所有信息模块入口,输出每个链接的 HTTP 状态码和页面标题。
# link_probe.py import requests from bs4 import BeautifulSoup BASE_URL = "http://localhost:8080/blog" links = { "我的文章": "/blog/my_article.jsp", "我的相册": "/blog/my_album.jsp", "我的资料": "/blog/my_profile.jsp", "我的好友": "/blog/my_friends.jsp", } with requests.Session() as session: # 先登录,拿到会话凭证 session.post(f"{BASE_URL}/login", data={"username": "Xyx2ss", "password": "Xyx2ss"}) for name, path in links.items(): resp = session.get(f"{BASE_URL}{path}") title = BeautifulSoup(resp.text, "html.parser").title.string if resp.ok else "N/A" print(f"{name}: {resp.status_code} | {title}")用 Session 先登录再访问,是为了保证请求携带了有效的会话 Cookie。status_code 只告诉你服务端是否响应,页面真实内容还要靠 title 确认,如果状态码是 200 但 title 是空的,说明页面内容没有正常渲染,和报告里的空白页是同一类问题。
4.3 后台管理的会话与权限校验
博客后台管理包括用户管理、文章管理、相册管理和修改管理员密码,这些功能只对管理员开放。测试时要验证两件事:管理员登录后所有功能可操作,未登录或普通用户登录时不能通过直接改 URL 绕过权限。
| 功能模块 | 操作 | 预期输出 | 关键校验点 |
|---|---|---|---|
| 用户管理 | 查询、修改用户信息 | 显示用户列表并可编辑 | 仅管理员可见入口 |
| 文章管理 | 查询、修改文章 | 显示文章列表并可编辑 | 操作前校验 session 角色 |
| 相册管理 | 查询、修改相册 | 显示相册列表并可编辑 | 操作前校验 session 角色 |
| 修改管理员密码 | 提交新密码 | 提示修改成功 | 旧密码必须校验;修改后重新登录 |
权限测试的要点是:功能入口隐藏不等于权限安全。很多系统只是把后台管理入口不显示在页面上,但 URL 仍然可以直接访问。测试时应该直接构造后台 URL 去请求,观察服务端是否返回 403 或跳转登录页。如果返回了 200 和完整页面,说明缺少过滤器或拦截器做角色校验,这是中高风险缺陷。
5. 集成测试中的 500 错误与页面空白:缺陷定位和日志排查实战
5.1 为什么单元测试过了,集成阶段还能大面积报错
报告里的测试流程是先进首页,登录成功后进入功能模块,最后访问后台管理。问题集中在集成阶段:页面链接信息缺失、首页显示紊乱、多个功能区 500。这种现象在模块连接时很常见。单元测试是单模块运行,数据库连接、session 传递、过滤器链都不会完整加载;集成后所有模块共用一个上下文,任何一环配置不对都会暴露。
常见原因有三类。第一类是接口契约不一致,比如登录模块把用户信息存进了 session,信息模块却从 request 的 attribute 里取值,拿不到就抛空指针。第二类是过滤器链顺序问题,权限过滤器先于登录过滤器执行,未登录请求直接被拦截,页面渲染时拿不到当前用户,模板渲染失败返回 500。第三类是数据依赖,每个模块单元测试时用的是自己造的测试数据,集成后数据混在一起,外键关联出错。
5.2 先看日志再猜问题:catalina.out 里的三条线索
出现 500 时最容易犯的错是直接刷新页面反复看现象,正确做法是先找服务端日志。当年的 JSP 应用大多跑在 Tomcat 上,日志集中在 catalina.out。我一般的排查顺序是:先看异常类型,再看堆栈里的类名,最后定位到具体 JSP 或 Servlet。
# 查看最近 100 行服务端日志 tail -n 100 /usr/local/tomcat/logs/catalina.out # 按时间窗口过滤当天异常,输出完整堆栈 grep "2011-11-27" /usr/local/tomcat/logs/catalina.out | grep -A 20 "Exception" # 单独抓空指针和 SQL 异常,常见 500 元凶 grep -E "NullPointerException|SQLException" /usr/local/tomcat/logs/catalina.out | tail -n 50tail 适合看最新请求产生的日志,grep 加时间窗口适合回看某个测试时段的完整异常记录。重点看三种异常:NullPointerException 通常是 JSP 里取了不存在的对象属性,SQLException 通常是表名或字段和数据库不一致,ClassNotFoundException 则是缺少依赖的 jar 包。报告里的多个 500 如果能落到某一种异常类型上,根因立刻清晰。
5.3 缺陷登记表与代码层根因推断
测试报告的结果部分只提到“发现了问题并提出了解决方法”,没有给出具体的缺陷清单。按报告中的现象可以整理出一张缺陷登记表。
| 缺陷位置 | 缺陷现象 | 可能根因 | 建议动作 |
|---|---|---|---|
| 推荐博客区 | 500 | 查询语句引用了不存在的字段 | 核对 SQL 与数据表结构 |
| 热门文章区 | 空白 | JSP 编译失败或转发异常 | 单独访问该 JSP 看报错信息 |
| 主页面信息 | 空白 | 模板数据源为空 | 检查页面依赖的数据对象是否注入 |
| 我的文章等四入口 | 500 | 会话中拿不到用户信息 | 检查登录后的 session 写入逻辑 |
| 首页显示紊乱 | 样式丢失 | CSS 或静态资源路径错误 | 用浏览器开发者工具看资源加载状态 |
首页显示紊乱是个容易被忽略但很重要的现象。状态码可能正常,但页面没有样式,说明 CSS 引用路径在集成环境下失效了。单元测试时页面通常直接用物理路径访问,集成后多了应用上下文路径,原本的css/style.css需要改成${pageContext.request.contextPath}/css/style.css。这类问题不会出现在功能测试里,必须通过页面呈现效果来确认。
6. 测试报告的反推价值:用三条检查线索复核需求覆盖率
6.1 线索一:从用例表反推导航完整性
测试报告的首页显示模块列出了六个测试项,但对照功能结构图可以发现缺了登录入口和用户注册入口的用例,而这恰恰是报告描述里最重要的功能。复核时先把所有页面链接罗列出来,再和用例表做对比,缺失的页面就是测试盲区。
6.2 线索二:从异常现象反推会话与鉴权设计
多个模块同时 500 或空白,不能只盯着页面代码,要回看公共依赖。用浏览器开发者工具查看请求头里的 Cookie,确认登录后是否拿到了 sessionId;再在服务端打印 session 中的用户属性,看是否在跳转过程中被清空。这类问题在当年的 JSP 项目里十有八九是 session 传参不规范。
6.3 线索三:用需求跟踪矩阵量化覆盖缺口
把功能点、测试用例、执行结果放进一张矩阵表,能直接算出覆盖率。
| 功能模块 | 设计用例数 | 执行通过 | 存在缺陷 | 覆盖率 |
|---|---|---|---|---|
| 首页显示 | 6 | 2 | 4 | 33% |
| 登录模块 | 7 | 7 | 0 | 100% |
| 信息模块 | 4 | 0 | 4 | 0% |
| 后台管理 | 4 | 待执行 | 待执行 | 未统计 |
缺口最大的信息模块正好对应集成测试的集中失败区。复核脚本可以用 grep 统计用例文件中每个模块标记的用例数量,快速得到覆盖率数字。
# 统计各模块用例数量,快速定位测试薄弱点 grep -c "test_homepage" test_cases.py grep -c "test_login" test_cases.py grep -c "test_info_module" test_cases.py grep -c "test_admin" test_cases.py这四条命令把四个模块的用例数分别数出来,和需求跟踪矩阵里的设计用例数对比。如果脚本里的用例数量明显少于矩阵中的设计数,说明用例没有全部落成可执行脚本,回归时只能靠手工补测,覆盖率数据不可信。把矩阵和脚本数字对上,才算把这份测试报告真正用透。
本文还有配套的精品资源,点击获取