一、项目介绍
该云论坛系统是基于 Spring 前后端分离架构开发的论坛系统,采用 Java 语言开发,后端集成 SpringBoot、SpringMVC、MyBatis 等框架,前端使用 HTML、CSS、JavaScript、jQuery 等技术,数据库采用 MySQL 5.7,支持用户注册登录、帖子管理、站内私信、个人中心等核心功能。
本次测试旨在通过自动化测试方式,验证系统各模块功能可用性、界面合理性、性能稳定性及安全性,确保系统上线后能为用户提供流畅、安全的使用体验。测试范围覆盖功能测试、界面测试、性能测试、易用性测试、兼容性测试、安全测试,通过 Selenium 编写自动化测试脚本实现核心场景的自动化验证。
二、测试计划
2.1 测试用例设计
测试用例从 6 个核心方面设计,全面覆盖系统功能与非功能需求:
- 功能测试:验证核心业务流程与功能点的正确性
- 界面测试:检查界面元素布局、视觉体验的合理性
- 性能测试:评估高并发场景下系统响应与稳定性
- 易用性测试:验证操作流程简洁性与用户引导有效性
- 兼容性测试:适配不同浏览器及设备访问场景
- 安全测试:保障用户数据安全与系统防攻击能力
2.2测试用例图

2.3执行测试用例
2.3.1登录页面功能
测试场景一:输入正确的用户名和正确密码,点击登录
预期结果:登录成功,直接跳转到论坛首页

实际结果:登录成功,进入论坛首页

测试场景二:输入错误的用户名和密码,点击登录
预期结果:出现警告,用户名或密码错误

实际结果:出现警告

测试场景三:输入正确用户名和错误密码,点击登录
预期结果:出现警告,用户名或密码错误

实际结果:出现警告

测试场景四:输入错误的用户名和正确的密码,点击登录
预期结果:出现警告,用户名或密码错误

实际结果:出现警告

测试场景五:不输入用户名和密码,点击登录
预期结果:出现提醒:用户名和密码不能为空
实际结果:

测试场景六:输入用户名,不输入密码,点击登录
预期结果:出现提醒:密码不能为空

实际结果:

测试场景七:不输入用户名,输入密码,点击登录
预期结果:出现提醒:用户名不能为空

实际结果:

2.3.2注册页面功能
测试场景一:输入用户名,昵称,密码,勾选同意条款进行注册
预期结果:注册成功,跳转到登录页面,输入正确的账号和密码就登录成功跳转到论坛首页

实际结果:注册成功,跳转登录页面

输入正确的用户名和密码可以正常登录:

跳转论坛首页:

测试场景二:不输入用户名,输入昵称、密码、勾选同意条款,点击注册
预期结果:注册失败,出现提醒用户名不能为空

实际结果:出现提醒

测试场景三:输入用户名,昵称,密码和确认密码不一致,勾选同意条款点击注册
预期结果:注册失败,出现提醒:请检查确认密码

实际结果:

测试场景四:输入用户名,昵称,密码,不勾选同意协议,点击登录
预期结果:注册失败,勾选同意条例框变红
实际结果:

2.3.3论坛首页的功能测试
测试场景一:检查页面元素
预期结果:菜单栏、帖子分类模块、搜索框、主题切换、私信小铃铛、用户信息、帖子列表页、发布帖子按钮、浏览数、点赞数、回复数
实际结果:

测试场景二:点击帖子分类版块
预期结果:跳转到对应的帖子模块
实际结果:
Java版块:

经验分享版块:

测试场景三:点击发布帖子按钮
预期结果:跳转到发布帖子页面
实际结果:

测试场景四:点击查看帖子
预期结果:跳转到帖子详情页,展示信息有:标题、作者、发布日期、帖子内容、发私信按钮、点赞按钮、回复框、回复按钮
实际效果:

测试场景五:使用搜索框输入已存在帖子的标题进行搜索
预期结果:在页面中显示该贴字和带有此关键字的帖子

实际结果:页面跳回论坛首页

结论:BUG –> 搜索框出现BUG,点击回车后页面没有出现预期结果而是跳回论坛首页
测试场景六:点击主题切换
预期结果:如果当前主题是白天模式,系统回转成夜间模式,如果是夜间模式会转成白天模式
实际结果:


结论:夜间模式出现BUG,云论坛logo没能显示出来,没有跟随系统切换
2.3.4论坛帖子详情页的功能测试
测试场景一:检查帖子详情页页面元素
预期效果:帖子标题、帖子发布时间、浏览数、点赞数、评论数、头像、作者、发私信按钮、帖子内容、点赞按钮、回复工具栏、回复框、回复按钮
实际效果:


测试场景二:测试发私信功能,输入文本发送私信
预期效果:出现发送成功提示
站内信页面:


实际结果:

测试场景三:测试回复信息功能,输入文本,点击回复
预期效果:出现提示:回复成功,回复的信息展示在页面上
回复页面:

实际效果:


测试场景四:测试回复信息功能,不输入文本,点击回复
预期效果:出现提示:请输入回复内容
回复页面:

实际效果:

测试场景五:点击点赞按钮
预期效果:出现成功提醒:点赞成功
页面:

实际效果:

2.3.5我的帖子页面测试
测试场景一:点击右上角头像处,再点击我的帖子
预期效果:我的昵称、头像、发帖数量、邮箱、注册日期、帖子列表、个人介绍
页面展示:


实际效果:

测试场景二:在个人帖子里点击发布的帖子
预期效果:跳转到该帖子的详情页,元素齐全
实际效果:


2.3.6个人中心页面测试
测试场景一:点击右上角头像处,再点击个人中心
预期效果:用户昵称、头像、修改头像按钮、昵称输入框+修改按钮、邮箱地址输入框+修改按钮、电话号码输入框+修改按钮、原密码输入框、新密码输入框、确认密码输入框,提交修改按钮、个人简介框、修改按钮
页面展示:

实际效果:


测试场景二:修改密码,输入正确原密码,新密码和确认密码一致,点击提交修改
预期结果:修改成功,跳转到登录页面
页面展示:

实际效果:修改成功

使用新密码登录:

登录成功,跳转首页:

测试场景三:修改密码,输入正确原密码,新密码和确认密码不一致
预期结果:修改失败,出现两次输入的密码不相同提示

实际效果:出现密码不同提示,确认密码输入框变大、变亮

测试场景四:修改密码,输入错误原密码,新密码和确认密码一致
预期结果:修改失败,出现参数校验失败警告

实际效果:原密码、新密码、确认密码输入框都被清空,出现参数校验失败警告

测试场景五:修改密码,不输入原密码,输入一致的新密码和确认密码
预期结果:修改失败,出现提示

实际效果:没有提示弹窗出现,光标在原密码输入框,原密码输入框变大、高亮
测试场景六:修改个人简介,在个人简介框输入文本
预期效果:修改成功

实际效果:

2.3.7帖子发布页面测试
测试场景一:检查发布帖子的页面元素
预期效果:版块框,版块下拉表、标题输入框、内容编辑工具栏、内容编辑输入框、发布按钮
实际效果:



测试场景二:选择版块,输入文本标题,输入帖子内容,点击发布按钮
预期结果:帖子发布成功,在论坛首页、我的帖子处可以查看发布的新帖子

实际效果:发布成功,页面跳转到首页,新发布的帖子出现在首页

点击我的帖子查看新发帖子:

测试场景三:选择版块,不输入标题,输入内容,点击发布
预期效果:发帖失败,出现提示:请输入帖子标题

实际效果:出现请输入帖子标题提示,标题输入框变大、变亮

测试场景四:选择版块,输入标题,不输入内容,点击发布按钮
预期结果:发帖失败,出现提示:请输入帖子内容

实际效果:

三、自动化测试
3.1测试环境与工具
- 开发环境:IntelliJ IDEA、JDK 21、Maven 3.8.x
- 测试工具:Selenium 4.9.0、webdriver-manager 5.6.2
- 浏览器:Edge 浏览器(配合 EdgeDriver)
其他浏览器驱动创建可参考:自动化测试浏览器驱动
3.2创建驱动对象
我们需要测试页面和功能有很多,但是我们的云论坛项目必须要先登录才能访问每个页面和功能,如果在每段测试功能的代码中都创建一次驱动对象进行登录那就很繁琐,我们可以把这部分的代码提取出来为Utils.java文件放在创建的common包下面,需要创建驱动对象时继承这个java文件即可。创建tests包用来存放测试不同页面的测试文件,images包用来存放测试过程中的截图。

创建驱动对象代码:
public class Utils {
public static WebDriver driver = null;
//显示等待
public WebDriverWait wait = null;
public Utils(String url){
driver = createDriver();
//调用driver
driver.get(url);
//创建显示等待对象
wait = new WebDriverWait(driver, Duration.ofSeconds(3));
}
//创建驱动对象
private static WebDriver createDriver() {
if(driver == null) {
//下载驱动
System.setProperty("wdm.edgeDriverUrl", "https://msedgedriver.microsoft.com/");
WebDriverManager.edgedriver().setup();
EdgeOptions options = new EdgeOptions();
options.addArguments("–remote-allow-origins=*");
//创建驱动对象
driver = new EdgeDriver(options);
//添加隐式等待
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
}
return driver;
}
}
3.3登录功能测试类
3.3.1登录成功测试
package tests;
import common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.support.ui.ExpectedConditions;
import java.io.IOException;
public class LoginPage extends Utils {
public static String url = "http://127.0.0.1:58080/sign-in.html";
public LoginPage(){
super(url);
}
/**
* 检查登录页面是否可以正常打开
*/
public void checkPageRight(){
// 检查登录框
driver.findElement(By.cssSelector("#username"));
driver.findElement(By.cssSelector("#password"));
driver.findElement(By.cssSelector("#submit"));
driver.findElement(By.cssSelector("body > div > div > div > div:nth-child(1) > div > div.text-center.text-muted.mt-3 > a"));
}
/**
* 登录成功–正确的账号和密码
*/
public void LoginSuc() throws IOException {
//先清空输入框
driver.findElement(By.cssSelector("#username")).clear();
driver.findElement(By.cssSelector("#password")).clear();
driver.findElement(By.cssSelector("#username")).sendKeys("hh");
driver.findElement(By.cssSelector("#password")).sendKeys("admin");
driver.findElement(By.cssSelector("#submit")).click();
//屏幕截图
ScreenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
//校验登录是否成功—按钮
driver.findElement(By.cssSelector("#nav_board_index > a"));
}
}
登录成功,跳转首页:

3.3.2登录失败测试
public void LoginFail2(){
driver.findElement(By.cssSelector("#username")).sendKeys("hh");
driver.findElement(By.cssSelector("#submit")).click();
}
public void LoginFail3(){
driver.findElement(By.cssSelector("#submit")).click();
}
public void LoginFail4(){
driver.findElement(By.cssSelector("#username")).sendKeys("kkk");
driver.findElement(By.cssSelector("#password")).sendKeys("1111111");
driver.findElement(By.cssSelector("#submit")).click();
}
public void LoginFail5(){
driver.findElement(By.cssSelector("#username")).sendKeys("karry");
driver.findElement(By.cssSelector("#password")).sendKeys("1111111");
driver.findElement(By.cssSelector("#submit")).click();
}
public void LoginFail6(){
driver.findElement(By.cssSelector("#username")).sendKeys("kk");
driver.findElement(By.cssSelector("#password")).sendKeys("admin");
driver.findElement(By.cssSelector("#submit")).click();
}
public void LoginFail7(){
driver.findElement(By.cssSelector("#username")).sendKeys("bucunzai");
driver.findElement(By.cssSelector("#password")).sendKeys("000000");
driver.findElement(By.cssSelector("#submit")).click();
}
登录失败,弹出警告:

3.4注册功能测试类
3.4.1注册成功测试
package tests;
import common.Utils;
import org.openqa.selenium.By;
import javax.swing.plaf.SliderUI;
public class RegisterPage extends Utils {
public static String url = "http://127.0.0.1:58080/sign-up.html";
public RegisterPage() {
super(url);
}
/**
* 检查注册页面是否可以正常打开
*/
public void checkRegistPageRight(){
//检查注册框
driver.findElement(By.cssSelector("#username"));
driver.findElement(By.cssSelector("#nickname"));
driver.findElement(By.cssSelector("#password"));
driver.findElement(By.cssSelector("#passwordRepeat"));
driver.findElement(By.cssSelector("#policy"));
driver.findElement(By.cssSelector("#signUpForm > div > div:nth-child(6) > label > span"));
driver.findElement(By.cssSelector("#submit"));
}
/**
* 注册成功
*/
public void RegisterSuc() {
driver.findElement(By.cssSelector("#username")).sendKeys("喜羊羊");
driver.findElement(By.cssSelector("#nickname")).sendKeys("喜羊羊");
driver.findElement(By.cssSelector("#password")).sendKeys("123456");
driver.findElement(By.cssSelector("#passwordRepeat")).sendKeys("123456");
driver.findElement(By.cssSelector("#signUpForm > div > div:nth-child(6) > label > span")).click();
driver.findElement(By.cssSelector("#submit")).click();
//验证注册成功
driver.findElement(By.cssSelector("body > div > div > div > div:nth-child(1) > div > div.text-center.text-muted.mt-3"));
}
}
注册成功,跳转登录页面:

再次运行该测试数据时,弹出用户已存在警告:

3.4.2注册失败测试
/**
* 注册失败
* —-必填项缺失
* —-两次密码不同
* —-用户已存在
* —-用户名过长
* —-未勾选协议
*/
public void RegisterFail1(){
driver.findElement(By.cssSelector("#username")).sendKeys("myy");
driver.findElement(By.cssSelector("#password")).sendKeys("123456");
driver.findElement(By.cssSelector("#passwordRepeat")).sendKeys("123456");
driver.findElement(By.cssSelector("#signUpForm > div > div:nth-child(6) > label > span")).click();
driver.findElement(By.cssSelector("#submit")).click();
}
public void RegisterFail2() {
driver.findElement(By.cssSelector("#username")).sendKeys("羊羊");
driver.findElement(By.cssSelector("#nickname")).sendKeys("羊羊");
driver.findElement(By.cssSelector("#password")).sendKeys("123456");
driver.findElement(By.cssSelector("#passwordRepeat")).sendKeys("111111");
driver.findElement(By.cssSelector("#signUpForm > div > div:nth-child(6) > label > span")).click();
driver.findElement(By.cssSelector("#submit")).click();
}
public void RegisterFail3() {
driver.findElement(By.cssSelector("#username")).sendKeys("美羊羊");
driver.findElement(By.cssSelector("#nickname")).sendKeys("美羊羊");
driver.findElement(By.cssSelector("#password")).sendKeys("123456");
driver.findElement(By.cssSelector("#passwordRepeat")).sendKeys("123456");
driver.findElement(By.cssSelector("#signUpForm > div > div:nth-child(6) > label > span")).click();
driver.findElement(By.cssSelector("#submit")).click();
}
public void RegisterFail4() {
driver.findElement(By.cssSelector("#username")).sendKeys("喜羊羊111111111111111111111111111111111" +
"111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111" +
"111111111111111111111111111111111111111111111111");
driver.findElement(By.cssSelector("#nickname")).sendKeys("喜羊羊");
driver.findElement(By.cssSelector("#password")).sendKeys("123456");
driver.findElement(By.cssSelector("#passwordRepeat")).sendKeys("123456");
driver.findElement(By.cssSelector("#signUpForm > div > div:nth-child(6) > label > span")).click();
driver.findElement(By.cssSelector("#submit")).click();
}
public void RegisterFail5() throws InterruptedException {
driver.findElement(By.cssSelector("#username")).sendKeys("喜羊羊");
driver.findElement(By.cssSelector("#nickname")).sendKeys("喜羊羊");
driver.findElement(By.cssSelector("#password")).sendKeys("123456");
driver.findElement(By.cssSelector("#passwordRepeat")).sendKeys("123456");
driver.findElement(By.cssSelector("#submit")).click();
}
3.5论坛首页功能测试
3.5.1登录状态下论坛首页
package tests;
import common.AllLogin;
import common.Utils;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import java.io.IOException;
public class ListPageSuc extends Utils {
public static String url = "http://127.0.0.1:58080/index.html";
public ListPageSuc() {
super(url);
}
// public ListPageSuc() throws IOException {
// super();
// aLogin();
// }
public void checkForumList01() throws IOException {
//个人信息版块
String text1 = driver.findElement(By.cssSelector("body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.dropdown")).getText();
//帖子分类版块 — 首页
String text2 = driver.findElement(By.cssSelector("#nav_board_index > a")).getText();
//帖子分类版块 — Java
String text3 = driver.findElement(By.cssSelector("#topBoardList > li:nth-child(2) > a")).getText();
//帖子分类版块 — C++
String text4 = driver.findElement(By.cssSelector("#topBoardList > li:nth-child(3) > a")).getText();
//帖子分类版块 — 前端技术
String text5 = driver.findElement(By.cssSelector("#topBoardList > li:nth-child(4) > a")).getText();
//帖子分类版块 — MySQL
String text6 = driver.findElement(By.cssSelector("#topBoardList > li:nth-child(5) > a")).getText();
//帖子分类版块 — 面试宝典
String text7 = driver.findElement(By.cssSelector("#topBoardList > li:nth-child(6) > a")).getText();
//帖子分类版块 — 经验分享
String text8 = driver.findElement(By.cssSelector("#topBoardList > li:nth-child(7) > a")).getText();
//帖子分类版块 — 招聘信息
String text9 = driver.findElement(By.cssSelector("#topBoardList > li:nth-child(8) > a")).getText();
//帖子分类版块 — 福利待遇
String text10 = driver.findElement(By.cssSelector("#topBoardList > li:nth-child(9) > a")).getText();
//帖子分类版块 — 灌水区
String text11 = driver.findElement(By.cssSelector("#topBoardList > li:nth-child(10) > a")).getText();
//首页
String text12 = driver.findElement(By.cssSelector("#article_list_board_title")).getText();
/**
帖子列表板块
*/
driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div > div > div"));
//帖子标题
String title = driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate")).getText();
//帖子发布时间
String pushTime = driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-muted.mt-2 > div > div.col > ul > li:nth-child(2)")).getText();
//帖子作者
String author = driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-muted.mt-2 > div > div.col > ul > li:nth-child(1)")).getText();
//发布帖子
String text13 = driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div > div.col-auto.ms-auto.d-print-none > div > a.btn.btn-primary.d-none.d-sm-inline-block.article_post")).getText();
//搜索框
driver.findElement(By.cssSelector("body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.d-none.d-md-flex.me-3 > div > form > div > input"));
//主题切换
driver.findElement(By.cssSelector("body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-dark"));
//信息提示
driver.findElement(By.cssSelector("body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > div > a"));
//校验文本
// System.out.println(text1);System.out.println(text2);System.out.println(text3);System.out.println(text4);System.out.println(text5);System.out.println(text6);
// System.out.println(text7);System.out.println(text8);System.out.println(text9);System.out.println(text10);System.out.println(text11);System.out.println(text12);System.out.println(text13);
assert !title.isEmpty();
assert !pushTime.isEmpty();
assert !author.isEmpty();
//截图
ScreenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
}
}
运行结果符合预期,可以进入论坛首页,截图结果:


3.5.2未登录状态下论坛首页
package tests;
import common.Utils;
import org.openqa.selenium.By;
public class ListPageUn extends Utils {
//未登录状态 — 跳回登录页面
public static String url = "http://127.0.0.1:58080/index.html";
public ListPageUn() {
super(url);
}
public void unListPage(){
driver.findElement(By.cssSelector("#username"));
}
}
结果跳转登录页面:

3.6论坛帖子详情页测试类
3.6.1检查详情页元素
public class PostDetailPage extends Utils {
public static String url = "http://127.0.0.1:58080/index.html";
public PostDetailPage() {
super(url);
}
// public PostDetailPage() throws IOException {
// super();
// aLogin();
// }
public void CheckPostRight() throws IOException, InterruptedException {
//点击帖子
driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(2) > div > div.col > div.text-truncate > a > strong")).click();
/**
* 帖子部分
*/
//标题
String title1 = driver.findElement(By.cssSelector("#details_article_title")).getText();
String title2 = driver.findElement(By.cssSelector("#details_article_content_title")).getText();
//作者
String author = driver.findElement(By.cssSelector("#article_details_author_name")).getText();
//时间
String pushTime = driver.findElement(By.cssSelector("#details_article_createTime")).getText();
//内容
String text = driver.findElement(By.cssSelector("#details_article_content > p")).getText();
// String text = driver.findElement(By.cssSelector("#details_article_content")).getText();
//浏览数
String views = driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div.col-auto.d-none.d-md-inline > ul > li:nth-child(2)")).getText();
//喜爱数
String likes = driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div.col-auto.d-none.d-md-inline > ul > li:nth-child(3)")).getText();
//回复数
String replies = driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div.col-auto.d-none.d-md-inline > ul > li:nth-child(4)")).getText();
//点赞按钮
String like = driver.findElement(By.cssSelector("#details_btn_like_count")).getText();
//编辑按钮
String edit = driver.findElement(By.cssSelector("#details_artile_edit")).getText();
//删除按钮
String del = driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div:nth-child(1) > div.col-9.card.card-lg > div.card-footer.bg-transparent.mt-auto.justify-content-end > div > div:nth-child(3) > div > a")).getText();
// 滚动到页面最底部
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("window.scrollTo(0, document.body.scrollHeight)");
Thread.sleep(2000); // 等待新内容加
// /**
// * 回复部分
// */
// //最新回复
// String reply1 = driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div:nth-child(2) > h3")).getText();
// //回复作者、内容、时间
// String replyAuthor = driver.findElement(By.cssSelector("#details_reply_area > div > div.col-3.card > div > h3 > a")).getText();
// String replyText = driver.findElement(By.cssSelector("#details_reply_area > div > div.col-9.card.card-lg > div.card-body")).getText();
// String replyTime = driver.findElement(By.cssSelector("#details_reply_area > div > div.col-9.card.card-lg > div.card-footer.bg-transparent.mt-auto")).getText();
// //回复框
// driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div:nth-child(4) > div > div.card-body"));
// //回复按钮
// String replyBtn = driver.findElement(By.cssSelector("#details_btn_article_reply")).getText();
//
// //校验
// assert !title1.isEmpty();
// assert !title2.isEmpty();
// assert !author.isEmpty();
// assert !pushTime.isEmpty();
// assert !text.isEmpty();
// assert !views.isEmpty();
// assert !likes.isEmpty();
// assert !replies.isEmpty();
// assert like.equals("点赞");
// assert edit.equals("编辑");
// assert del.equals("删除");
// assert reply1.equals("最新回复");
// assert !replyAuthor.isEmpty();
// assert !replyText.isEmpty();
// // System.out.println(replyText);
// assert !replyTime.isEmpty();
// assert replyBtn.equals("回复");
// System.out.println(text);
//截图
ScreenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
}
运行结果:

返回0说明页面元素都存在
3.6.2详情页功能测试
点赞功能、查看作者信息功能、编辑帖子功能、删除帖子功能
package tests;
import common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import java.io.IOException;
import java.lang.reflect.Field;
public class InteractionFunction extends Utils {
public static String url = "http://127.0.0.1:58080/index.html";
public InteractionFunction() {
super(url);
}
/**
* 点赞别人帖子
*/
public void giveLike1(){
//点击别人帖子,有时候可能是自己的帖子
driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong")).click();
//点赞前的赞数
int likes1 = Integer.parseInt(driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div.col-auto.d-none.d-md-inline > ul > li:nth-child(3)")).getText().trim());
//点赞
driver.findElement(By.cssSelector("#details_btn_like_count")).click();
//点赞后的赞数
int likes2 = Integer.parseInt(driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div.col-auto.d-none.d-md-inline > ul > li:nth-child(3)")).getText().trim());
//校验点赞数量变化
assert likes1 + 1 == likes2;
}
/**
* 点击作者跳转作者主页
*/
public void clickAuthor() throws IOException {
//点击帖子
driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong")).click();
//点击前的作者名
String name1 = driver.findElement(By.cssSelector("#article_details_author_name")).getText();
//点击作者
driver.findElement(By.cssSelector("#article_details_author_name")).click();
//点击后的作者名
String name2 = driver.findElement(By.cssSelector("#profile_nickname")).getText();
//截图
ScreenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
//校验作者名
assert name2.equals(name1);
}
/**
* 编辑帖子
*/
//检查编辑页面
public void checkEditPost() throws InterruptedException, IOException {
//点击帖子
driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong")).click();
//点击编辑
driver.findElement(By.cssSelector("#details_artile_edit")).click();
//编辑
String edit = driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div > div > h2")).getText();
//版块
String Section = driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div > div > div.card-body > div.row.align-items-center > div.col-2 > h2")).getText();
String mianshibaodian = driver.findElement(By.cssSelector("#edit_article_board_name")).getText();
//标题
String title = driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div > div > div.card-body > div:nth-child(3) > div.col-2 > h2")).getText();
//内容
String content = driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div > div > div.card-body > div:nth-child(4) > div.col-2 > h2")).getText();
//提交
String get = driver.findElement(By.cssSelector("#edit_article_submit > span")).getText();
//校验
assert edit.equals("编辑");
assert Section.equals("版块");
assert mianshibaodian.equals("面试宝典");
assert title.equals("标题");
assert content.equals("内容");
assert get.equals("提交");
//截图
ScreenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
}
//开始编辑
public void editPost() throws InterruptedException {
//点击帖子
//driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate")).click();
//点击编辑
//driver.findElement(By.cssSelector("#details_artile_edit")).click();
//编辑标题
driver.findElement(By.cssSelector("#edit_article_title")).sendKeys("编辑后");
//编辑内容
WebElement element = driver.findElement(By.cssSelector("#edit_article_content_area > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll"));
Actions actions = new Actions(driver);
actions.doubleClick(element).sendKeys("编辑后…………").perform();
Thread.sleep(2000);
//提交
driver.findElement(By.id("edit_article_submit")).click();
}
/**
* 删除帖子
*/
public void deletePost() throws IOException, InterruptedException {
//点击帖子
driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong")).click();
//点击删除按钮
driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div:nth-child(1) > div.col-9.card.card-lg > div.card-footer.bg-transparent.mt-auto.justify-content-end > div > div:nth-child(3) > div > a")).click();
//截图
ScreenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
Thread.sleep(2000);
//取消按钮
//driver.findElement(By.cssSelector("#details_delete_modal > div > div > div.modal-footer > div > div > div:nth-child(1) > a")).click();
//删除按钮
driver.findElement(By.cssSelector("#details_artile_delete")).click();
}
}
回复功能
public void replyPostSuc() throws InterruptedException {
SimpleDateFormat simp = new SimpleDateFormat("HHmmMM");
String replyTime = simp.format(System.currentTimeMillis());
String replyTest = "自动回复" + replyTime;
// //代码编辑器
// //清空回复编辑框
// driver.findElement(By.cssSelector("#article_details_reply > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre")).clear();
// //输入回复内容
// driver.findElement(By.cssSelector("#article_details_reply > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre")).sendKeys("性能测试是什么");
// //发送回复
// driver.findElement(By.cssSelector("#details_btn_article_reply")).click();
// //校验是否回复
// String replyText2 = driver.findElement(By.cssSelector("#details_reply_area > div > div.col-9.card.card-lg")).getText();
//// assert replyText2.equals("性能测试是什么");
// System.out.println(replyText2);
//通过原生的鼠标键盘操作来处理该元素-Actions
WebElement element = driver.findElement(By.cssSelector("#article_details_reply > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre"));
Actions actions = new Actions(driver);
//actions.doubleClick(element).perform();
actions.doubleClick(element).sendKeys(replyTest).perform();
Thread.sleep(2000);
//点击发布
driver.findElement(By.cssSelector("#details_btn_article_reply")).click();
Thread.sleep(2000);
//校验是否回复成功
String replyAfter = driver.findElement(By.cssSelector("#details_reply_area > div:nth-child(1) > div.col-9.card.card-lg > div.card-body")).getText();
System.out.println(replyAfter);
}
3.7私信功能测试类
3.7.1检查私信页面元素
package tests;
import common.Utils;
import org.openqa.selenium.By;
import java.io.IOException;
/**
* 单独登陆
*/
public class InboxPage extends Utils {
public static String url ="http://127.0.0.1:58080/sign-in.html";
public InboxPage() {
super(url);
}
public void inBox() throws IOException {
//登录
driver.findElement(By.cssSelector("#username")).sendKeys("张三");
driver.findElement(By.cssSelector("#password")).sendKeys("123456");
driver.findElement(By.cssSelector("#submit")).click();
//点击站内信
driver.findElement(By.cssSelector("body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > div > a > svg")).click();
//截图
ScreenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
//点击私信查看
driver.findElement(By.cssSelector("#index_div_message_list > div:nth-child(1) > div > div.col.text-truncate > a")).click();
//截图
ScreenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
//点击回复
driver.findElement(By.cssSelector("#btn_index_message_reply")).click();
//截图
ScreenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
//输入回复内容
driver.findElement(By.cssSelector("#index_message_reply_receive_content")).sendKeys("元旦快乐");
//点击发送
driver.findElement(By.cssSelector("#btn_index_send_message_reply")).click();
}
}
3.7.2发送私信功能
package tests;
import common.Utils;
import org.openqa.selenium.By;
public class InSiteMessage extends Utils {
public static String url ="http://127.0.0.1:58080/index.html";
public InSiteMessage() {
super(url);
}
/**
* 发送私信
*/
//1. 通过帖子发送私信
public void SendMessage1() throws InterruptedException {
//点击帖子
driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong")).click();
//点击发私信
driver.findElement(By.cssSelector("#btn_details_send_message")).click();
Thread.sleep(1000);
//取消
//driver.findElement(By.cssSelector("#index_message_receive_cancel")).click();
//发送
driver.findElement(By.cssSelector("#index_message_receive_content")).sendKeys("周一好");
driver.findElement(By.cssSelector("#btn_index_send_message")).click();
}
//2. 通过作者主页发送私信
public void SendMessage2() throws InterruptedException {
//点击帖子
driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong")).click();
//点击作者
driver.findElement(By.cssSelector("#article_details_author_name")).click();
//点击发私信
driver.findElement(By.cssSelector("#btn_profile_send_message")).click();
Thread.sleep(2000);
//发送
driver.findElement(By.cssSelector("#index_message_receive_content")).sendKeys("周二好");
driver.findElement(By.cssSelector("#btn_index_send_message")).click();
}
}
私信输入框有文本,发送成功:


3.8发布新帖功能测试类
3.8.1发帖成功
选择版块、输入合法标题、输入内容
/**
* 发帖成功
*/
public void PostedSuc() throws InterruptedException {
//选择版块
driver.findElement(By.cssSelector("#article_post_borad > option:nth-child(5)")).click();
//输入标题
driver.findElement(By.cssSelector("#article_post_title")).sendKeys("软件测试工程师");
//输入内容
WebElement element = driver.findElement(By.cssSelector("#edit-article > div.CodeMirror.cm-s-default.CodeMirror-wrap.CodeMirror-empty > div.CodeMirror-scroll"));
Actions actions = new Actions(driver);
actions.doubleClick(element).sendKeys("软件测试工程师养成路线……………").perform();
Thread.sleep(2000);
//发布
driver.findElement(By.id("article_post_submit")).click();
//校验
String text = driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate")).getText();
assert text.equals("软件测试工程师");
}

3.8.2发帖失败
1.只有版块,没有标题、没有内容
2.有版块、有内容、没有标题
3.有版块、有标题、没有内容
4.有版块、有内容、标题过长
/**
* 发帖失败
*/
//1. 直接点击发布
public void PostedFail1(){
WebElement element = driver.findElement(By.id("article_post_submit"));
element.click();
}
//2. 标题为空有内容
public void PostedFail2() throws InterruptedException {
//输入内容
WebElement element = driver.findElement(By.cssSelector("#edit-article > div.CodeMirror.cm-s-default.CodeMirror-wrap.CodeMirror-empty > div.CodeMirror-scroll"));
Actions actions = new Actions(driver);
actions.doubleClick(element).sendKeys("自动化测试1229自动化测试1229自动化测试1229自动化测试1229自动化测试1229").perform();
//下滑屏幕
// 滚动到页面最底部
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("window.scrollTo(0, document.body.scrollHeight)");
Thread.sleep(2000); // 等待新内容加
//点击发布
driver.findElement(By.id("article_post_submit")).click();
}
//3. 有标题没内容
public void PostedFail3(){
//输入标题
driver.findElement(By.cssSelector("#article_post_title")).sendKeys("软件测试工程师");
//点击发布
driver.findElement(By.id("article_post_submit")).click();
}
//4. 有内容,帖子标题过长
public void PostedFail4() throws InterruptedException {
//输入标题
driver.findElement(By.cssSelector("#article_post_title")).sendKeys("软件测试工程师软件测试工程师软件测试工程师软件测试工程师软件测试" +
"工程师软件测试工程师软件测试工程师软件测试工程师软件测试工程师工程师软件测试工程师软件测试工程师软件测试工程师软件测试工程师工程师软件测试工程师软件测试工程师软件测试工程师软件测试工程师工程师软件测试工程师软件测试工程师软件测试工程师软件测试工程师");
//输入内容
WebElement element = driver.findElement(By.cssSelector("#edit-article > div.CodeMirror.cm-s-default.CodeMirror-wrap.CodeMirror-empty > div.CodeMirror-scroll"));
Actions actions = new Actions(driver);
actions.doubleClick(element).sendKeys("自动化测试1229自动化测试1229自动化测试1229自动化测试1229自动化测试1229").perform();
Thread.sleep(2000);
//点击发布
driver.findElement(By.id("article_post_submit")).click();
}
3.9我的帖子页面测试类
3.9.1检查页面元素
/**
* 检查我的帖子页
*/
public void myPosts() throws IOException {
//头像
driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div > div:nth-child(2)"));
//作者
driver.findElement(By.cssSelector("#profile_nickname"));
//发帖数
driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div > div.col > div > div:nth-child(1)"));
//邮箱
driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div > div.col > div > div:nth-child(2)"));
//注册日期
driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div > div.col > div > div:nth-child(3)"));
//帖子
driver.findElement(By.cssSelector("#profile_article_body > li:nth-child(1) > div.card.timeline-event-card > div"));
//个人介绍
driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div.col-lg-4 > div > div > div > div"));
String text = driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div.col-lg-4 > div > div > div > div > h2")).getText();
assert text.equals("个人介绍");
//查看帖子
String title1 = driver.findElement(By.cssSelector("#profile_article_body > li:nth-child(1) > div.card.timeline-event-card > div > div > div > div > div.text-truncate > a > strong")).getText();
driver.findElement(By.cssSelector("#profile_article_body > li:nth-child(1) > div.card.timeline-event-card > div > div > div > div > div.text-truncate > a > strong")).click();
String title2 = driver.findElement(By.cssSelector("#details_article_content_title")).getText();
//截图
ScreenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
assert title1.equals(title2);
}
返回结果无异常,页面元素都存在
3.10个人中心页面测试类
3.10.1检查个人中心页面元素
/**
* 检查个人中心页
*/
public void personalCenter() throws IOException {
//个人中心
driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div > div > h2"));
//我的账户
driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div > div.col-3.d-none.d-md-block.border-end > div > div > a"));
//昵称
driver.findElement(By.cssSelector("#settings_nickname"));
//头像
driver.findElement(By.cssSelector("#settings_avatar"));
//修改头像
driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div > div.col.d-flex.flex-column > div > div.row.align-items-center > div:nth-child(2) > a"));
//昵称修改
driver.findElement(By.cssSelector("#setting_input_nickname"));
driver.findElement(By.cssSelector("#setting_submit_nickname"));
//修改邮箱地址
driver.findElement(By.cssSelector("#setting_input_email"));
driver.findElement(By.cssSelector("#setting_submit_email"));
//修改电话号码
driver.findElement(By.cssSelector("#setting_input_phoneNum"));
driver.findElement(By.cssSelector("#setting_submit_phoneNum"));
//修改密码
driver.findElement(By.cssSelector("#settings_input_oldPassword"));
driver.findElement(By.cssSelector("#settings_input_newPassword"));
driver.findElement(By.cssSelector("#settings_input_passwordRepeat"));
//提交修改
driver.findElement(By.cssSelector("#settings_submit_password"));
//个人简介
driver.findElement(By.cssSelector("#settings_textarea_remark"));
driver.findElement(By.cssSelector("#settings_submit_remark"));
//截图
ScreenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
}
返回结果无异常,页面元素都存在
3.10.2个人中心页修改密码
修改密码成功,原密码正确,新密码与确认密码一致
//3-改密码成功
driver.findElement(By.cssSelector("#settings_input_oldPassword")).sendKeys("123123");
driver.findElement(By.cssSelector("#settings_input_newPassword")).sendKeys("123456");
driver.findElement(By.cssSelector("#settings_input_passwordRepeat")).sendKeys("123456");
driver.findElement(By.cssSelector("#settings_submit_password")).click();
修改密码失败:
1.原密码错误
2.新密码与确认密码不一致
//1-原密码错误
driver.findElement(By.cssSelector("#settings_input_oldPassword")).sendKeys("123456");
driver.findElement(By.cssSelector("#settings_input_newPassword")).sendKeys("111111");
driver.findElement(By.cssSelector("#settings_input_passwordRepeat")).sendKeys("111111");
driver.findElement(By.cssSelector("#settings_submit_password")).click();
//2-新密码不一致
driver.findElement(By.cssSelector("#settings_input_oldPassword")).sendKeys("123123");
driver.findElement(By.cssSelector("#settings_input_newPassword")).sendKeys("111111");
driver.findElement(By.cssSelector("#settings_input_passwordRepeat")).sendKeys("222222");
driver.findElement(By.cssSelector("#settings_submit_password")).click();
四、BUG
4.1搜索框不能使用
输入帖子列表存在的关键字回车搜索没有结果

4.2修改头像功能无效
点击修改头像,跳转上传图片文件,上传图片后头像依旧没有变化


五、性能测试
5.1度线程组配置
采用JMeter 步进线程组(Stepping Thread Group) 模拟真实用户流量:
- 总线程数:200 个(模拟 200 并发用户)
- 启动规则:初始等待 0 秒,每 5 秒新增 10 个线程(1 秒内完成线程准备)
- 负载保持:线程数达到 200 后,持续满载运行 20 秒
- 停止规则:满载结束后,每秒停止 10 个线程,平滑降低负载
同时在测试计划中配置了CSV 数据文件、HTTP 请求默认值、Cookie 管理器等组件,保障接口请求的参数化与会话保持。


5.2活跃线程趋势
- 线程数呈阶梯式上升:每 5 秒新增 10 线程,约 100 秒后达到 200 线程的满载状态
- 满载阶段稳定保持 20 秒:无线程突增 / 突降的异常波动
- 停止阶段平滑下降:每秒减少 10 线程,避免服务器瞬时压力骤减

5.3响应时间
- 低负载阶段(前 30 秒):所有接口响应时间均低于 100ms,性能表现稳定
- 高负载阶段(线程数≥100 后):响应时间明显上升,部分接口(如发帖、登录)耗时突破 500ms
- 峰值阶段(满载 20 秒):响应时间波动加剧,最长耗时达到 700ms,说明服务器资源已接近瓶颈

5.4吞吐量
- 线程上升阶段:TPS 随线程数增加同步增长,最高达到约 540 次 / 秒
- 满载阶段:TPS 出现波动下降,稳定在 60 次 / 秒左右,说明服务器已无法处理更多请求
- 线程下降阶段:TPS 随负载降低逐步回升,但未恢复至峰值水平,侧面体现高负载对系统的短暂影响

5.5吞吐量与响应时间
对比响应时间图与吞吐量图,可明确两者的负相关关系:
- 低负载期(线程数<50):响应时间稳定(<100ms),吞吐量随线程数增加同步上升 —— 此时服务器资源充足,请求可快速处理,处理能力随用户量增长而提升
- 中负载期(50≤线程数<150):响应时间开始缓慢上升(100ms→300ms),吞吐量增长速率放缓 —— 服务器资源逐渐紧张,请求排队等待处理,处理效率开始下降
- 高负载期(线程数≥150):响应时间急剧上升(300ms→700ms),吞吐量反而下降 —— 服务器达到资源瓶颈,大量请求堆积,“处理单个请求的耗时增加” 导致 “单位时间内处理的总请求数减少”
- 负载下降期:响应时间快速回落,吞吐量逐步回升 —— 服务器压力减轻后,请求排队减少,处理效率恢复,但因高负载的短暂影响,未立即回到峰值水平
服务器负载在阈值内时,吞吐量随用户量增长而提升;超过阈值后,响应时间会显著增加,同时吞吐量下降。
5.6聚合报告
- 登录接口性能最弱:平均耗时 101ms,是所有接口中最高的,且异常率达 11.16%
- 大部分接口表现稳定:首页、注册等接口平均耗时低于 50ms,异常率在 10% 左右
- 系统总体吞吐量:1875.5 次 / 秒,满足中小型论坛的基础并发需求,但高负载下需优化

六、性能测试报告
部分数据展示:

展示测试的元数据:
- 测试数据文件:yuntest2.jtl(JMeter 的测试结果文件)
- 测试开始时间:1/15/26, 9:34 PM
- 测试结束时间:1/15/26, 9:37 PM
APDEX 应用性能指数区:
APDEX 是衡量应用性能满意度的指标(范围 0~1,越接近 1 性能越好),这里配置了:
- 容忍阈值(T):500ms(响应时间≤500ms 为 “满意”)
- 挫败阈值(F):1 秒 500ms(响应时间≥1.5s 为 “挫败”)
各接口的 APDEX 值都在 0.8 以上(大部分接近 0.9/1.0),说明系统整体性能满意度较高,仅少数接口(如 “登录接口” APDEX 0.896)略低。
用饼图展示请求的成功 / 失败占比:
- PASS(成功):92.16%(大部分请求正常完成)
- FAIL(失败):7.84%(约 7.84% 的请求业务逻辑 / 响应异常)
统计详情区:
这是报告的核心数据,包含各接口的关键性能指标(以 “总体” 和部分接口为例):
| 指标 | 含义说明 |
| Requests | 请求名称(对应测试计划中的接口) |
| Executions | 总请求次数 |
| Error% | 该接口的异常率(仅统计业务失败,和聚合报告的“异常率”范围不同) |
| Average | 平均响应时间(ms) |
| Min/Max | 最小/最大响应时间(ms) |
| 90%95%99% | 响应时间百分位(比如90%的请求响应时间≤对应数值) |
| Throughput | 吞吐量(次/分钟) |
| Network(KB/s) | 网络传输速率(KB/秒) |
重点接口数据示例:
- 登录接口:总请求 24711 次,异常率 9.09%,平均响应时间 47.79ms—— 是异常率较高的接口之一
- 发布新帖子:总请求 24710 次,异常率 7.61%,平均响应时间 36.07ms
- 总体:总请求 26931 次,异常率 7.84%(和请求汇总的 FAIL 占比一致),平均响应时间 84.74ms,吞吐量 1525.12 次 / 分钟
云论坛完整自动化测试脚本以及性能测试报告皆上传到Gitee,链接:
自动化测试
性能测试






