《博客系统测试报告》
一、引言
1.1 编写目的
本次博客系统测试报告旨在全面评估博客系统的整体质量,通过对博客系统各项功能(如个人博客列表、分页功能、文章发布、编辑、删除,用户注册登录等)非功能特性的严格测试,发现系统中存在的缺陷和不足。报告期望为个人博客系统提供详细、准确的测试结果和分析,为后续系统的开发和维护工作提供有力依据,以确保博客系统能够稳定、高效地运行。
1.2 背景
- 本个人博客系统是基于前后端分离模式开发的学习实践项目,主要用于练习主流技术栈的整合与实战。
- 后端采用 Java 生态,基于 Spring Boot 框架搭建,整合了 Spring MVC 处理请求响应、Spring AOP 实现日志与事务管理,并使用 Redis 优化数据访问性能,数据库选用 MySQL 存储博客内容及用户信息。
- 前端通过 HTML、CSS 和 JavaScript 实现基础交互,包含登录页、注册页、博客列表页、博客详情页、编辑页等核心页面,整体架构简洁清晰,覆盖了个人博客的基础功能。
1.3 核心功能实现
- 用户模块:实现了用户注册登录(含密码加密)、注销功能,通过拦截器实现强制登录校验,确保未登录用户无法访问编辑等敏感操作。
- 博客模块:支持博客的创建、编辑、删除,编辑页通过模拟键盘操作(如 Ctrl+A 全选清除)提升交互体验,博客详情页可展示标题、内容、浏览量、发布时间及作者信息。
- 技术实践:尝试了前后端数据交互(如表单提交、JSON 数据传输)、浏览器弹窗处理(Alert)及简单的自动化测试(使用 Selenium 编写测试用例验证核心流程)。
- 待完善点:头像功能暂未实现、用户信息文章数、分类数、未与后端同步、缺少仓库链接、为实现评论点赞功能;
二、测试范围
2.1 功能测试
本次测试覆盖了个人博客系统的核心功能模块,结合自动化测试与手动验证,确保基础功能的可用性和用户交互的流畅性。
- 用户注册:验证用户名/ 密码格式校验、重复注册提示;
- 登录功能:验证账号密码正确性、登录状态保持(Cookie/Session);
- 强制登录:未登录用户访问编辑页 / 个人中心,是否跳转至 登录页面;
- 文章发布:标题 / 内容输入、Markdown 格式支持、提交按钮交互;
- 文章编辑: 修改已发布文章的标题 / 内容, 验证修改后是否实时更新;
- 文章删除:删除操作后的二次确认、删除后列表是否同步更新;
- 分页功能:验证个人列表页、主页的首页、尾页、上一页、下一页功能;
2.2 非功能测试
2.2.1 性能测试
- 页面加载性能测试: 测试首页、博客详情页、登录页面等核心页面的加载速度与响应时间。
- 并发访问性能测试:模拟多用户同时访问首页、浏览文章、注册、发布文章等操作,检测系统高并发下的性能表现。
- 数据库操作性能测试:验证数据库在大量数据查询和高频写入操作时的处理效率与稳定性。
- 压力测试:进行长时间高负载压力测试及弱网环境下的性能测试,评估系统稳定性和容错能力。
2.2.2 界面测试
- 登录界面:检查页面布局、文本错别、加载报错、输入框长度、按钮位置及设计风格是否合规。
- 注册界面:验证表单布局合理性,保证标签与输入框对应;检查文本完整性与提示清晰度;测试响应式设计适配效果;确保视觉元素对齐;检查颜色对比度合规性。
- 个人列表界面:审查信息展示布局,避免多余空白或拥挤;检查静态元素;确保字体样式一致;测试无博客时访问列表页的显示情况。
- 博客列表界面:保证列表项布局统一,分页控件正常显示。
- 博客详情界面:确保内容排版合理,发布时间和阅读量正确展示。
- 博客编辑界面:检查 MarkDown 编辑器布局、发布按钮功能及占位符提示。
2.2.3 安全测试
- 身份认证与授权安全测试:检测弱密码使用、防范暴力破解攻击、保障会话管理安全、防止权限越界访问。
- 数据传输安全测试:验证 HTTPS 协议应用,确保数据传输过程中的完整性。
- 数据存储安全测试:检查数据库加密状态,测试数据库备份与恢复功能的有效性。
- 输入验证与漏洞防范测试:开展 SQL 注入攻击测试和 XSS 攻击防范验证,避免系统存在安全漏洞。
2.2.4 易用性测试
- 导航与操作便捷性:检验导航栏(含登录、注册按钮)易用性,测试系统在多设备上的操作适配情况。
- 输入与反馈友好性:查看表单输入提示是否明确、实时输入校验是否生效、操作反馈是否清晰。
- 信息可读性与理解性:评估文本排版可读性和错误信息的易懂程度。
- 用户引导和帮助:测试新用户引导功能、检查帮助文档入口是否便捷、验证操作撤销与恢复功能是否可用。
2.2.5 兼容性测试
浏览器兼容性测试:测试 Chrome、Firefox、Safari、Edge 新旧版本访问兼容性
操作系统兼容性测试:验证博客系统在 Windows 7/10/11 和 macOS Monterey 系统的访问兼容性。
三、测试环境
3.1 硬件环境and软件环境
设备类型 | 配置参数 | 用途 |
---|---|---|
开发服务器(本地) | CPU:Intel Core i5-10400(6 核 12 线程) 内存:16GB DDR4 硬盘:512GB SSD 系统:Windows 10 64 位 | 运行后端服务(Spring Boot)、数据库(MySQL 8.0)、缓存(Redis) |
设备类型 | 型号 / 配置 | 操作系统 | 浏览器版本 | 屏幕分辨率 |
---|---|---|---|---|
笔记本电脑 | 拯救者 R7000P CPU:AMD Ryzen 7 5800H with Radeon Graphics 3.20 GHz 显卡:NVIDIA GeForce RTX 3050 Ti Laptop GPU | Windows 10 专业版 | Chrome 135.0.7049.96(正式版本) (64 位) Microsoft Edge版本 126.0.2592.61 (正式版本) (64 位) | 1920×1080(默认) |
3.2 测试工具
工具 | 优势 |
---|---|
JAVA | 强类型语言,生态成熟,适合大型测试框架扩展(如 JUnit、TestNG)。 |
IDEA | 智能代码补全、调试工具强大,支持 Maven/Gradle 依赖管理,提升开发效率。 |
Selenium | 主流 Web 自动化测试框架,支持跨浏览器(Chrome/Firefox/Safari)、多语言(Java/Python)。 |
WebDriverManager | 自动管理浏览器驱动,无需手动下载 / 配置chromedriver.exe ,自动匹配版本,跨平台兼容(Windows/macOS/Linux)。 |
Selenium 是一款开源的 Web 自动化测试工具,支持跨浏览器(Chrome/Firefox/Safari 等)和多编程语言(Java/Python/C# 等),广泛用于 Web 应用的功能测试与回归测试。
IntelliJ IDEA 是一款由 JetBrains 公司开发的智能集成开发环境(IDE),专为 Java、Kotlin 以及多种其他编程语言设计。它具备智能代码补全、实时语法检查、高效调试等功能,还拥有丰富的插件生态。凭借强大的代码分析与重构能力,能显著提升开发效率,是开发者的得力助手。
四、 测试计划
4.1 测试用例
4.2 功能测试
4.2.1 各功能模块测试概述
功能模块 | 测试用例总数 | 通过用例数 | 失败用例数 | 通过率 |
---|---|---|---|---|
注册测试 | 7 | 7 | 0 | 100% |
登录测试 | 9 | 8 | 1 | 88.9% |
个人列表测试 | 13 | 13 | 0 | 100% |
博客编辑测试 | 7 | 6 | 1 | 85.7% |
主页测试 | 6 | 6 | 0 | 100% |
4.2.2 详细测试结果
针对每个功能模块,详细描述测试过程中发现的问题,包括问题描述、出现的页面或操作步骤、预期结果、实际结果、问题严重程度(如严重、一般、轻微)和问题优先级(如高、中、低)。
(1)注册功能
测试结果
测试编号 | 测试场景 | 操作步骤 | 预期结果 | 实际结果 | 是否符合预期 | 问题描述(若不符) | 严重程度 | 优先级 |
---|---|---|---|---|---|---|---|---|
REG-01 | 正常注册-跳转 | 1. 进入注册页面 2. 用户名输入testuser01,密码和确认密码输入 123.com 3. 点击提交按钮 4. 弹窗点击确认按钮 | 1. 系统提示:恭喜:注册成功!是否要跳转到登陆页面? 2. 跳转到登录页面 | 1. 系统提示 “注册成功”; 2. 成功跳转到登录页面。 | 是 | |||
REG-02 | 正常注册-不跳转 | 1. 进入注册页面 2. 用户名输入testuser02,密码和确认密码输入 123.com 3. 点击提交按钮 4. 弹窗点击取消按钮 | 1. 系统提示:恭喜:注册成功!是否要跳转到登陆页面? 2. 在注册页面 | 1. 系统提示 “注册成功”; 2. 还在注册界面 | 是 | |||
REG-03 | 用户名为空注册 | 1. 进入注册页; 2. 用户名留空,密码输入123.com,确认密码输入 123.com; 3. 点击 “注册” 按钮。 | 系统提示 “请先输入用户名!”,注册失败,页面停留在注册页。 | 提示 “请先输入用户名!”,注册失败,页面停留在注册页。 | 是 | |||
REG-04 | 密码为空注册 | 1. 进入注册页; 2. 用户名输入testuser04,密码清空,确认密码清空; | 系统提示 “请先输入密码!”,注册失败,页面停留在注册页。 | 提示 “请先输入密码!”,注册失败,页面停留在注册页。 | 是 | |||
REG-05 | 用户名已存在 | 1. 进入注册页; | 系统提示 “该用户已存在,请重新输入!”,注册失败。 | 系统提示 “该用户已存在,请重新输入!”,注册失败。 | 是 | |||
REG-06 | 密码和确认密码不同 | 1. 进入注册页; 2. 用户名输入 testuser06,密码输入 123.com,确认密码输入 123123; 3. 点击 “注册” 按钮。 | 系统提示 “两次密码不一致,请重新输入!”,注册失败。 | 系统提示 “两次密码不一致,请重新输入!”,注册失败。 | 是 | |||
REG-07 | 退出注册按钮 | 1. 进入注册页; 2. 点击退出; | 页面跳转到登录页面 | 跳转到登录页面 | 是 |
部分操作截图
注册页面
REG-01
(2)登录功能
测试编号 | 测试场景 | 操作步骤 | 预期结果 | 实际结果 | 是否符合预期 | 问题描述(若不符) | 严重程度 | 优先级 |
---|---|---|---|---|---|---|---|---|
LOG-01 | 正常登录-跳转 | 1. 进入登录页面 2. 用户名输入admin,密码输入 admin 3. 点击提交按钮 | 1. 系统验证通过,跳转到个人博客页 2. 页面显示用户信息和博客列表 | 1. 成功跳转到个人博客页; 2.显示用户名和博客信息。 | 是 | |||
LOG-02 | 用户名为空 | 1. 进入登录页面 2. 用户名清空,密码输入 admin 3. 点击提交按钮 | 1. 系统弹窗提示:请先输入用户名! 2. 页面停留在登录页 | 显示输入用户名弹窗,页面停留在登录页 | 是 | |||
LOG-03 | 用户名不存在 | 1. 进入登录页面 2. 用户名输入admin100,密码输入 admin 3. 点击提交按钮 | 1. 弹窗显示:用户名不存在, 2. 页面停留在登录页面 | 弹窗提示用户名或密码错误,与预期提示不符 | 否 | 提示信息不准确,未明确指出用户名不存在 | 一般 | 中 |
LOG-04 | 密码错误 | 1. 进入登录页面 2. 用户名输入admin,密码输入 admin123 3. 点击提交按钮 | 系统弹出提示框,显示 “用户名或密码错误”,页面停留在登录页。 | 弹窗提示用户名或密码错误,页面停留在登录页 | 是 | |||
LOG-05 | 点击注册 | 1. 进入登录页面; 2. 点击 “注册” 按钮。 | 页面跳转到注册页面。 | 页面成功跳转到注册页面。 | 是 | |||
LOG-06 | 点击主页或 LOGO / 知我博客(未登录) | 1. 处于登录页面; 2. 点击主页链接、LOGO 或 “知我博客” 文字。 | 页面跳转到博客主页(未登录状态)。 | 页面成功跳转到博客主页,显示未登录状态的内容。 | 是 | |||
LOG-07 | 登录状态保持 - 长时间未操作后点击个人列表页 | 1. 使用正确账号密码登录系统; 2. 等待超过系统设定的无操作时长; 3. 点击个人列表页链接; | 页面跳转到登录页面。 | 页面跳转到登录页面。 | 是 | |||
LOG-08 | 强制登录 - 未登录访问编辑页 | 1. 不进行登录操作; 2. 直接在地址栏输入编辑页的 URL 并访问。 | 页面跳转到登录页面。 | 页面跳转到登录页面。 | 是 | |||
LOG-09 | 强制登录 - 未登录访问个人编辑页 | 1. 不进行登录操作; 2. 直接在地址栏输入个人编辑页的 URL 并访问。 | 页面跳转到登录页面。 | 页面跳转到登录页面。 | 是 |
操作截屏
登录界面
LOG-02:用户名为空
LOG-03:用户名不存在
LOG-04: 密码错误
(3)个人博客页
测试编号 | 测试场景 | 操作步骤 | 预期结果 | 实际结果 | 是否符合预期 | 问题描述(若不符) | 严重程度 | 优先级 |
---|---|---|---|---|---|---|---|---|
BLOG-01 | 文章数验证 | 1. 登录账号,进入个人博客页; 2. 查看页面显示的文章数。 | 显示的文章数与用户实际已发布的文章数量一致。 | 页面显示文章数为 15,但实际用户已发布文章数为 15。 | 是 | |||
BLOG-02 | 检验已发布文章标题、发布时间、摘要正常显示 | 1. 登录账号,进入个人博客页; 2. 查看每篇已发布文章的标题、发布时间和摘要。 | 标题、发布时间和摘要没有乱码,正常显示。 | 没有出现乱码 | 是 | |||
BLOG-03 | 分页按钮 - 点击首页按钮 | 1. 登录账号,进入个人博客页且当前不在第一页; 2. 点击 “首页” 按钮。 | 页面跳转到第一页。 | 跳转到第一页 | 是 | |||
BLOG-04 | 分页按钮 - 点击上一页按钮 | 1. 登录账号,进入个人博客页且当前不在第一页; 2. 点击 “上一页” 按钮。 | 页面跳转到上一页。 | 跳转到上一页 | 是 | |||
BLOG-05 | 分页按钮 - 点击尾页按钮 | 1. 登录账号,进入个人博客页且当前不在最后一页; 2. 点击 “尾页” 按钮。 | 页面跳转到最后一页。 | 跳转到最后一页 | 是 | |||
BLOG-06 | 分页按钮 - 点击下一页按钮 | 1. 登录账号,进入个人博客页且当前不在最后一页; 2. 点击 “下一页” 按钮。 | 页面跳转到下一页。 | 跳转到下一页 | 是 | |||
BLOG-07 | 文章操作 - 点击查看全文按钮 | 1. 登录账号,进入个人博客页; 2. 随机选择一篇文章,点击 “查看全文” 按钮。 | 页面跳转到博客详情页,显示文章标题、发布时间、内容、阅读量等信息。 | 显示博客标题、发布时间、内容、阅读量 | 是 | |||
BLOG-08 | 文章操作 - 点击修改文章按钮 | 1. 登录账号,进入个人博客页; 3. 修改文章标题和内容点击修改 4. 确认修改提示窗 | 1. 页面跳转到博客编辑页; 2. 显示文章的标题和内容 3. 修改后点击提交。 4. 文章标题和内容摘要展示在个人列表页。 5. 文章发布时间发生改变 | 1. 成功跳转到博客编辑页,成功显示文章标题和内容; 2. 个人列表页文章更新,发布时间更新 | 是 | |||
BLOG-09 | 文章操作 - 点击删除文章按钮 | 1. 登录账号,进入个人博客页; 2. 随机选择一篇文章,点击 “删除文章” 按钮; 3. 确认删除提示框。 | 文章从个人列表页删除。 | 文章从个人列表页消失 | 是 | |||
BLOG-10 | 点击我的主页 | 1. 登录账号,进入个人博客页; 2. 点击 “我的主页” 按钮。 | 页面跳转到我的列表页第一页。 | 跳转到我的列表页第一页 | 是 | |||
BLOG-11 | 点击写博客 | 1. 登录账号,进入个人博客页; 2. 点击 “写博客” 按钮。 | 页面跳转到博客编辑页。 | 跳转到写博客页面,标题和内容为空 | 是 | |||
BLOG-12 | 点击退出登录 | 1. 登录账号,进入个人博客页; 2. 点击 “退出登录” 按钮。 | 退出登录状态,页面跳转到登录页面。 | 退出登录状态,并跳转到登录页面 | 是 | |||
BLOG-13 | 点击主页或 LOGO / 知我博客 | 1. 登录账号,进入个人博客页; 2. 点击主页链接、LOGO 或 “知我博客” 文字。 | 页面跳转到主页(已登录状态) | 页面跳转到主页,登录状态 | 是 |
部分操作截屏
个人列表页
BLOG-02:检验已发布文章标题、发布时间、摘要正常显示
BLOG-08:文章操作 - 点击修改文章按钮
BLOG-07:文章操作 - 点击查看全文按钮
(4)文章编辑页
测试编号 | 测试场景 | 操作步骤 | 预期结果 | 实际结果 | 是否符合预期 | 问题描述(若不符) | 严重程度 | 优先级 |
---|---|---|---|---|---|---|---|---|
EDIT-01 | 标题 / 内容正常输入并发布 | 1. 登录后进入编辑页; 2. 输入标题 “测试文章”,内容 “正文内容”; 3. 点击 “发布文章” 按钮。 | 1. 系统提示 “发布成功”; 2. 跳转至博客详情页,显示标题和内容。 | 发布成功,跳转至博客列表页,内容正确显示。 | 否 | 发布成功后没有跳转到博客详情页 | 一般 | 中 |
EDIT-02 | MarkDown 格式支持验证 | 1. 在内容区输入 MarkDown 格式文本:# 一级标题\n- 列表项1\n[链接](https://example.com) ;2. 发布后查看详情页。 | 详情页显示正确渲染的 MarkDown 内容:一级标题加粗、列表项缩进、链接可点击。 | 详情页正确渲然MarkDown内容 | 是 | |||
EDIT-03 | 标题为空发布 | 1. 登录后进入编辑页; 2. 内容区输入 “正文内容”,标题留空; 3. 点击 “发布文章” 按钮。 | 系统提示 “标题不能为空”,阻止发布,页面停留在编辑页。 | 提示:请先输入标题!页面停留在编辑页 | 是 | |||
EDIT-04 | 内容为空发布 | 1. 登录后进入编辑页; 2. 输入标题 “测试标题”,内容区留空; 3. 点击 “发布” 按钮。 | 系统提示 “请输入文章内容!”,阻止发布,页面停留在编辑页。 | 提示:请输入文章内容!页面停留在编辑页 | 是 | |||
EDIT-05 | 点击“我的主页” | 1. 登录账号,进入个人博客页; 2. 点击 “我的主页” 按钮。 | 页面跳转到我的列表页第一页。 | 跳转到我的列表页第一页 | 是 | |||
EDIT-06 | 点击“退出登录” | 1. 登录账号,进入个人博客页; 2. 点击 “退出登录” 按钮。 | 退出登录状态,页面跳转到登录页面。 | 退出登录状态,并跳转到登录页面 | 是 | |||
EDIT-07 | 点击主页或 LOGO / 知我博客 | 1. 登录账号,进入个人博客页; 2. 点击主页链接、LOGO 或 “知我博客” 文字。 | 页面跳转到主页(已登录状态) | 页面跳转到主页,登录状态 | 是 |
部分操作截屏
博客编辑页
EDIT-02:MarkDown 格式支持验证
EDIT-04:内容为空发布
(5)主页
测试编号 | 测试场景 | 操作步骤 | 预期结果 | 实际结果 | 是否符合预期 | 问题描述(若不符) | 严重程度 | 优先级 |
---|---|---|---|---|---|---|---|---|
MAIN-01 | 文章标题 / 时间 / 摘要正常显示 | 1. 进入主页面(未登录 / 已登录); 2. 查看所有显示的文章条目。 | 标题、发布时间、摘要无乱码,格式正确(如时间为 “YYYY-MM-DD HH:mm”,摘要截断合理)。 | 所有文章没有乱码、时间格式也都正确 | 是 | |||
MAIN-02 | 点击首页按钮 | 1. 进入主页面(未登录 / 已登录)且当前不存在第一页; 2. 点击首页按钮。 | 跳转到主页第一页 | 成功跳转到第一页 | 是 | |||
MAIN-03 | 点击上一页按钮 | 1. 进入主页面(未登录 / 已登录)且当前不在第一页; 2. 点击上一页按钮。 | 跳转到上一页 | 成功跳转到上一页 | 是 | |||
MAIN-04 | 点击尾页按钮 | 1. 进入主页面(未登录 / 已登录)且当前不在最后一页; 2. 点击尾页按钮。 | 跳转到尾页 | 成功跳转到尾页 | 是 | |||
MAIN-05 | 点击下一页按钮 | 1. 进入主页面(未登录 / 已登录)且当前不在最后一页; 2. 点击下一页按钮。 | 跳转到下一页 | 成功跳转到下一页 | 是 | |||
MAIN-06 | 点击查看全文按钮 | 1. 进入主页面(未登录 / 已登录); 2. 在主页面点击任意文章的 “查看全文” 按钮。 | 跳转至博客详情页,显示完整标题、发布时间、内容、阅读量 | 正确显示标题、发布时间、内容、阅读量; | 是 |
部分操作截图
主页
MAIN-06: 点击查看全文按钮
4.3 自动化测试
4.3.1. 测试目标
- 验证博客系统核心功能(注册 / 登录、文章发布 / 编辑 / 删除、页面跳转、)的稳定性与正确性。
- 覆盖 前端交互逻辑 和 用户体验流程,减少人工回归测试成本,提升迭代效率。
- 确保新功能开发或代码变更时,不破坏已有功能(冒烟测试、回归测试)。
4.3.2 自动化测试用例
4.3.3 环境准备
因篇幅有限:全部代码链接为:博客自动化测试完整代码
1. 创建Maven项目并配置依赖
在IDEA中点击 File → New → Project ...→ Maven→next→输入项目名称和地址→Finish
2. 添加Maven依赖(pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>mycnblogTest</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><!-- 测试驱动下载 --><dependency><groupId>io.github.bonigarcia</groupId><artifactId>webdrivermanager</artifactId><version>5.8.0</version><scope>test</scope></dependency><!-- 安装selenium库 --><dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>4.0.0</version></dependency><!-- 安装屏幕截图库 --><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version></dependency></dependencies></project>
4.3.4 基础测试类 | Utils
说明:
- 根据自动化测试用例来编写代码,每一个页面一个测试类,然后在各个测试中进行测试用例的编写。
- 公共属性需要单独放一个类
------------------------------------------------------------------------------------
- common
- Utils.java ——公共类,创建驱动对象、强制等待、处理弹窗、屏幕截图
- tests
- LoginPage.java ——登录页面所有用例
- RegistrationPage.java ——注册页面所有用例
- MyListPage.java ——个人列表页面所有用例
- DetailPage.java ——详情页面所有用例
- EditPage.java ——编辑页面所有用例
- HomePage.java ——主页所有用例
- iamge
- 2025-04-10 ——存放当天的屏幕截图
基础公共类中存放一些公共属性:loginUrl、driver;公共方法:屏幕截图、弹窗处理
创建驱动
public Utils(String url){driver = createDriver();wait = new WebDriverWait(driver, Duration.ofSeconds(5));driver.get(url);}/*** 获取驱动对象* @return*/public static WebDriver createDriver(){if(driver == null){// 下载驱动WebDriverManager.chromedriver().setup();ChromeOptions options = new ChromeOptions();// 允许访问所以链接options.addArguments("--remote-allow-origins=*");// options.addArguments("-headless"); // 无头模式设置// 打开浏览器driver = new ChromeDriver(options);// 隐式等待driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));}return driver;}
屏幕截屏
/*** 屏幕截图* @param funcName* @throws IOException*/public void getScreensShot(String funcName) throws IOException {// ./src/test/java/images/2025-04-05/test01_17553033.pngSimpleDateFormat sim1 = new SimpleDateFormat("yyyy-MM-dd");SimpleDateFormat sim2 = new SimpleDateFormat("HHmmssSS");String dirTime = sim1.format(System.currentTimeMillis());String fileTime = sim2.format(System.currentTimeMillis());String fileName = "./src/test/java/images/" + dirTime + "/" + funcName + "_" + fileTime + ".png";File imageFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);FileUtils.copyFile(imageFile, new File(fileName));}
处理弹窗
/*** 处理弹窗 点击确认* @param expectedMessage 期望的弹窗消息*/public static void handleAlertAccept(String expectedMessage) {// 切换到警告弹窗Alert alert = wait.until(ExpectedConditions.alertIsPresent());// 获取实际的弹窗消息String actualMessage = alert.getText();// 关闭弹窗alert.accept();// 断言实际消息与期望消息是否一致assert actualMessage.equals(expectedMessage) : "实际弹窗消息与期望消息不符。实际消息: " + actualMessage + ", 期望消息: " + expectedMessage;}/*** 处理弹窗 点击取消* @param expectedMessage 期望的弹窗消息*/public static void handleAlertDismiss(String expectedMessage) {// 切换到警告弹窗Alert alert = wait.until(ExpectedConditions.alertIsPresent());// 获取实际的弹窗消息String actualMessage = alert.getText();// 关闭弹窗alert.dismiss();// 断言实际消息与期望消息是否一致assert actualMessage.equals(expectedMessage) : "实际弹窗消息与期望消息不符。实际消息: " + actualMessage + ", 期望消息: " + expectedMessage;}
4.3.4 登录页面测试 | LoginPage
登录页面测试:主要编写登录页面的测试用例、比如页面加载测试、正常登录、异常登录、注册/主页按钮测试
创建驱动访问登录页面
public class LoginPage extends Utils {public static String url = loginUrl;// 使用继承的方法实现访问urlpublic LoginPage(){super(url);}
}
正常登录:
public void logined(String username, String password){// 清空账号、密码driver.findElement(By.xpath("//*[@id=\"username\"]")).clear();driver.findElement(By.xpath("//*[@id=\"password\"]")).clear();driver.findElement(By.xpath("//*[@id=\"username\"]")).sendKeys(username);driver.findElement(By.xpath("//*[@id=\"password\"]")).sendKeys(password);driver.findElement(By.xpath("//*[@id=\"submit\"]")).click();wait.until(ExpectedConditions.urlToBe(myListUrl));WebElement web = driver.findElement(By.xpath("//*[@id=\"username\"]"));String actualMsg = web.getText();// 断言实际消息与期望消息是否一致assert actualMsg.equals(username) :"实际用户名与期望用户名不符。实际用户名: " + actualMsg + ", 期望用户名: " + username ;}
用户名或密码错误测试
注:文本输入框进行输入内容前先清空;不然上一次用例的内容会影响这次用例的测试;博主这就踩坑了。
// 用例4 账号错误 | 密码错误public void testWrongUserPwd(){// 清空账号、密码driver.findElement(By.xpath("//*[@id=\"username\"]")).clear();driver.findElement(By.xpath("//*[@id=\"password\"]")).clear();driver.findElement(By.xpath("//*[@id=\"username\"]")).sendKeys("admins");driver.findElement(By.xpath("//*[@id=\"password\"]")).sendKeys("1234567");driver.findElement(By.xpath("//*[@id=\"submit\"]")).click();// 强制等待弹窗出现wait.until(ExpectedConditions.alertIsPresent());// 处理错误的弹窗——警告弹窗String expectMsg = "用户名或密码错误!";handleAlertAccept(expectMsg);}
注册按钮点击测试
注意这种页面跳转测试,这种需要再一开始确保就在要测试的界面,不然会发现元素找不到,这可能是上一个用例跳转到了其他页面。
// 用例8 点击注册public void loginPageRegClick(){driver.get(url);driver.findElement(By.xpath("/html/body/div[1]/a[3]")).click();// 通过URL检测是否跳转到注册页面verifyUrlRedirection(regUrl);}
4.3.5 注册页面测试:| RegistrationPage
注册页面测试类:进行注册检测(异常,正常情况)、退出注册页面验证等注册页面用例测试
创建驱动访问注册页面
public class RegistrationPage extends Utils {public static String url = regUrl;private LoginPage login = new LoginPage();public RegistrationPage() {super(url);}// 测试用例
}
账号、密码、确认密码为空注册测试
public void empty(){// 清空操作clear();driver.findElement(By.xpath("//*[@id=\"submit\"]")).click();// 强制等待弹窗出现wait.until(ExpectedConditions.alertIsPresent());// 处理错误的弹窗——警告弹窗handleAlertAccept("请先输入用户名!");}
密码和确认密码为不同注册测试
public void differentPwds(){// 清空操作clear();driver.findElement(By.xpath("//*[@id=\"username\"]")).sendKeys("admin4");driver.findElement(By.xpath("//*[@id=\"password\"]")).sendKeys("123456");driver.findElement(By.xpath("//*[@id=\"password2\"]")).sendKeys("123453");driver.findElement(By.xpath("//*[@id=\"submit\"]")).click();// 强制等待弹窗出现wait.until(ExpectedConditions.alertIsPresent());// 处理错误的弹窗——警告弹窗String expectMsg = "两次密码不一致,请重新输入!";handleAlertAccept(expectMsg);}
用户名已存在
// 用例7: 用户名已存在注册测试public void existingUser(){// 清空操作clear();driver.findElement(By.xpath("//*[@id=\"username\"]")).sendKeys("admin");driver.findElement(By.xpath("//*[@id=\"password\"]")).sendKeys("123456");driver.findElement(By.xpath("//*[@id=\"password2\"]")).sendKeys("123456");driver.findElement(By.xpath("//*[@id=\"submit\"]")).click();// 强制等待弹窗出现wait.until(ExpectedConditions.alertIsPresent());// 处理错误的弹窗——警告弹窗String expectMsg = "该用户已存在,请重新输入!";handleAlertAccept(expectMsg);}
成功注册测试、跳转到登录页面
注意:注册用户名需要不同,不然会下一次运行就出错了,这里可以使用时间戳。
// 用例9: 成功注册测试、跳转到登录页面public void regSuc2(){driver.get(regUrl);clear();// 设计用户名String timestamp = new SimpleDateFormat("yyyyMMddHHmmssSS").format(System.currentTimeMillis());String userName = "user_" + timestamp;String password = "123.com";driver.findElement(By.xpath("//*[@id=\"username\"]")).sendKeys(userName);driver.findElement(By.xpath("//*[@id=\"password\"]")).sendKeys(password);driver.findElement(By.xpath("//*[@id=\"password2\"]")).sendKeys(password);driver.findElement(By.xpath("//*[@id=\"submit\"]")).click();// 显示等待弹窗出现wait.until(ExpectedConditions.alertIsPresent());// 关闭弹窗,跳转到登录页面handleAlertAccept("恭喜:注册成功!是否要跳转到登陆页面?");Alert alert = driver.switchTo().alert();alert.accept();// 检查当前页面是否还在注册页面verifyUrlRedirection(loginUrl);// 登录login.logined(userName, password);}
4.3.6 博客列表页测试 | MyListPage
博客列表页测试类:主要实现博客列表页的测试,例:博客列表显示、个人信息显示、分页按钮测试、查看文章测试等等;
创建驱动访问博客列表页
public class MyListPage extends Utils {private static final Logger LOGGER = Logger.getLogger(MyListPage.class.getName());public MyListPage() {super(myListUrl);}// 测试用例
}
分页按钮测试-上一页
public void testPreviousPageButton(){// 获取当前URL中的页码参数int currentPage = getCurrentPage();WebElement prePageBtn = wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@id=\"artDiv\"]/div[2]/button[2]")));if(currentPage == 1){// 首页prePageBtn.click();// 等待弹窗的出现handleAlertAccept("当前已经在首页了");} else if (currentPage > 1){// 非首页情况prePageBtn.click();// 上一页URL匹配wait.until(ExpectedConditions.urlContains("pindex=" + (currentPage - 1)));int newPage = getCurrentPage();if(newPage != currentPage - 1){LOGGER.log(Level.SEVERE, "上一页跳转失败, 期望页码: " + (currentPage - 1) +", 实际页码: " + newPage);}}}
分页按钮测试-下一页
// 用例6 分页按钮测试-下一页public void testNextPageButton() {String xpath = "//*[@id=\"artDiv\"]/div[2]/button[4]";int total = getTotalPages(xpath);// System.out.println("一共:" + totalPages + "页");int currentPage = getCurrentPage();WebElement nextPageBtn = wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@id=\"artDiv\"]/div[2]/button[3]")));if(currentPage == total){// 当前是尾页,点击下一页会有弹窗提醒nextPageBtn.click();handleAlertAccept("当前已经在尾页了");} else {// 非尾页情况nextPageBtn.click();// 跳转到下一页wait.until(ExpectedConditions.urlContains("pindex=" + (currentPage + 1)));int newPage = getCurrentPage();if(newPage != currentPage + 1){LOGGER.log(Level.SEVERE, "下一页跳转失败, 期望页码: " + (currentPage - 1) +", 实际页码: " + newPage);}}}
查看全文按钮测试
// 用例7 查看全文按钮测试public void testViewFullTextButton(){driver.findElement(By.xpath("//*[@id=\"artList\"]/div[1]/div[4]/a[1]")).click();// 等待页面加载完成wait.until(ExpectedConditions.urlContains("blog_content.html"));// 验证页面是否加载了全文内容WebElement fullTextContent = wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@id=\"editorDiv\"]")));assert fullTextContent.isDisplayed() : "查看全文按钮测试失败: 全文内容未显示";}
4.3.7 博客主页测试 | HomePage
HomePage类: 主要编写博客主页的测试用例,例:分页按钮检查、博客显示检查、登录和未登录不同检查。
验证已发布文章标题、发布时间、摘要正常显示
public void testBlogListDisplay(){// 检查博客标题WebElement titleElement = wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@id=\"artList\"]/div[1]/div[1]")));assert titleElement.isDisplayed() : "文章标题未正常显示";// 检查博客发布时间WebElement timeElement = wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@id=\"artList\"]/div[1]/div[2]")));assert timeElement.isDisplayed() : "文章发布时间未正常显示";// 检查博客内容WebElement contentElement = wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@id=\"artList\"]/div[1]/div[3]")));assert contentElement.isDisplayed() : "文章内容未正常显示";}
登录时显示退出登录,未登录时显示登录按钮
public void testLoginAndLoginOut(){// 检测登录按钮存在WebElement loginButton = wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@id=\"login-link\"]")));assert "登录".equals(loginButton.getText()) : "登录按钮显示异常";// 登录操作loginButton.click();wait.until(ExpectedConditions.urlToBe(loginUrl));LoginPage login = new LoginPage();login.loginSuc();wait.until(ExpectedConditions.urlToBe(myListUrl));// 检测退出登录按钮存在driver.get("http://localhost:8085/blog_list.html");wait.until(ExpectedConditions.urlToBe(homePageUrl));WebElement loginOutButton = wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@id=\"login-link\"]")));assert "退出登录".equals(loginOutButton.getText()) : "退出登录按钮显示异常";loginOutButton.click();handleAlertAccept("确认退出?");wait.until(ExpectedConditions.urlToBe(loginUrl));// 登录,便于后面测试login.loginSuc();}
4.3.8 博客详情页 | DetailPage
DetailPage类:主要编写博客详情页的测试用例, 例:文章展示是否正常、文章阅读量是否正常;
文章阅读量显示测试
public void testArticleViewCount(){// 获取文章初始阅读量WebElement viewCountElement = wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@id=\"rcount\"]")));int initialViewCount = Integer.parseInt(viewCountElement.getText());driver.get(url);wait.until(ExpectedConditions.urlToBe(url));// 再次获取文章阅读量viewCountElement = wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@id=\"rcount\"]")));int currentViewCount = Integer.parseInt(viewCountElement.getText());assert currentViewCount == (initialViewCount + 1) : "文章阅读显示异常";}
4.3.9 博客编辑页 | WriteBlogPage
WriteBlogPage类:主要编写博客编辑页的测试用例,例如:编辑博客页能否正常打开,异常博客编写、正常博客编写并发布;
成功发布文章
注意:Markdown编辑器无法通过clear()来删除内容,可以通过键盘事件来模仿Ctrl+A+Delete;
public void testArticleSuccessSubmit(){WebElement titleInput = wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@id=\"title\"]")));WebElement submitButton = wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("/html/body/div[2]/div[1]/button")));WebElement contentEditor = wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@id=\"editorDiv\"]/div[1]/div[6]/div[1]/div/div/div/div[5]/div/pre/span/span")));String title = "《解锁博客系统新体验:高效创作与个性化展示》";String content = "在当今数字化内容创作时代,博客已成为人们分享观点、交流思想的重要平台。你是否渴望在博客系统中轻松输出优质内容?如今的博客系统已实现 “质” 的飞跃,从编辑创作到展示分享,全方位助力你的内容之旅。\n" +"# 一、智能编辑:Markdown 加持,创作无忧\n" +"智能编辑器支持 Markdown 语法,让排版变得轻松快捷。无需复杂的操作,通过简单的符号和命令,就能快速完成标题、列表、链接、图片等元素的排版。无论是撰写技术文档,还是分享生活感悟,都能高效完成。而且,编辑器还具备自动保存功能,实时保存你的创作内容,告别意外丢失内容的烦恼,让你专注于创作本身。\n" +"# 二、个性模板:一键切换,风格随心\n" +"系统拥有丰富的模板库,无论是极简风、复古范,还是时尚潮流、文艺清新等风格,都能一键切换。你可以根据博客主题和个人喜好,选择最适合的模板,让你的博客独具风格,给读者带来全新的视觉体验。即使没有专业的设计技能,也能轻松打造出高颜值的博客页面。\n" +"# 三、高效管理:标签分类,精准查找\n" +"通过标签和分类管理功能,你可以对博客内容进行有序整理。给每篇文章添加合适的标签和分类,方便后续查找和管理。当积累了大量内容后,无论是自己回顾,还是读者搜索,都能快速定位到感兴趣的文章,大大提高了内容的查找效率。\n" +"# 四、智能推荐:精准推送,拓展阅读\n" +"更惊喜的是,系统还能根据用户浏览数据,运用智能算法精准推送感兴趣的文章。一方面,帮助作者扩大内容传播范围,让优质文章被更多人发现;另一方面,为读者提供个性化的阅读推荐,满足不同的阅读需求,拓展阅读视野,实现作者与读者之间的高效连接。\n" +"快来体验这款功能强大的博客系统,开启你的高效创作与精彩展示之旅,让每一次创作都能绽放光彩!";titleInput.sendKeys(title);Actions action = new Actions(driver);// 模拟全选操作(Ctrl + A) + Deleteaction.keyDown(contentEditor, Keys.CONTROL).sendKeys(contentEditor, "a").keyUp(contentEditor, Keys.CONTROL).sendKeys(contentEditor, Keys.DELETE).perform();action.sendKeys(contentEditor, content).perform();submitButton.click();// 处理|确认提交?|弹窗handleAlertAccept("确认提交?");// 处理 是否继续添加文章弹窗handleAlertDismiss("恭喜: 文章添加成功!是否继续添加文章?");wait.until(ExpectedConditions.urlToBe(myListUrl));// 通过标题验证是否添加成功WebElement currentTitleEle = wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@id=\"artList\"]/div[1]/div[1]")));String currentTitle = currentTitleEle.getText();assert currentTitle.equals(title) : "文章添加异常,期望标题: " + title + ", 实际标题: " + currentTitle;driver.quit();}
五、测试结论
5.1 测试通过情况总结
经综合测试,个人博客系统未通过整体测试验收。目前存在多项严重问题:登录模块中,当用户名不存在时,系统无法明确提示用户,可能导致用户反复尝试却无法得知失败原因,影响注册登录流程;文章发布功能存在严重缺陷,成功发布后无法跳转至对应文章详情页,致使功能链路断裂,影响用户体验与使用效率 。
同时,系统在界面、安全、兼容、易用及性能方面均存在风险:界面布局存在留白过多问题,影响美观与操作便利性;安全性方面存在漏洞,可能引发用户信息泄露或非法访问;兼容性不足,无法在多种浏览器、操作系统及设备上稳定运行;易用性存在缺陷,用户操作不便;性能表现不佳,无法满足高并发或复杂操作场景需求。以上问题将显著降低用户使用意愿,增加系统安全隐患,若投入使用,可能导致用户流失、数据安全事故及口碑受损等严重后果,需立即修复优化。