> 技术文档 > 【测试报告】思绪网(Java+Selenium+Jmeter自动化测试)

【测试报告】思绪网(Java+Selenium+Jmeter自动化测试)


一、项目简介

       思绪网作为一种在线交流平台,支持用户在平台下发布文章,并进行讨论。主要由登录页面,论坛页面,帖子编辑页,帖子详情页等页面组成。

二、项目功能

1.登录页面:输入正确的账号密码进行登录,跳转博客列表页。

2.博客列表页:加载出全部博客,左边显示用户信息与博客信息,右上角可发布新博客、页等按钮,选择一个博客可以进入查看博客详细信息。

3.博客详情页:能查看博客完整内容,当前登录用户查看自己博客有编辑与删除按钮,其余用户仅有查看博客权限。

4.博客编辑页:输入标题、正文选择提交按钮发布博客。

5.更新博客页:现当前需要修改的博客信息(标题,正文),修改完成点击提交按钮。

6.删除博客:在详情页中点击删除博客,可以正确删除,不在出现在列表页。

三、测试计划

(一)功能测试

        1.测试用例设计

2.测试内容
(1)登陆页面

测试用例:

测试用例编号 操作 账号 密码 预期结果 1 输入正常的账号密码 zhangsan 123456 登录成功 2 输入错误的账号和密码 tt 333 登录失败,提示错误 3 输入正确的账号和错误的密码 zhangsan 332 登录失败,提示错误 4 输入错误的账号和正确的密码 tt 123456 登录失败,提示错误 5 不填写账号和密码     登录失败,提示错误 6 填写账号不填写密码 zhangsan   登录失败,提示错误 7 不填写账号填写密码   123456 登录失败,提示错误

测试结果:

①测试用例1:

结论:登录成功,跳转至论坛页面,与预期结果一致。

②测试用例2:

结论:显示用户名或密码错误,与预期结果不一致,没有提示账号/密码错误

③测试用例3:

结论:显示密码错误,与预期结果一致

④测试用例4:

结论:显示用户名不存在,与预期结果一致

⑤测试用例5:

结论:显示用户名小于4位,与预期结果一致。

 ⑥测试用例6:

结论:显示密码错误,与预期结果一致。

⑦测试用例7:

结论:显示用户名小于4位,与预期结果一致。

(2)博客列表页面
测试用例编号 操作 预期结果 1 点击Gitee地址可跳转 跳转到相应Git地址 2 点击查看全文 查看博客详情 3 点击右上角写博客 进入编写博客 4 点击右上角注销 用户退出 5 点击右上角主页 回到列表页 6 点击右上角发布新博客 进入编写博客

①测试用例1:

结论:正常跳转与预期一样

②测试用例2:

结论:与预期结果一样

③测试用例3:

结论:与预期一致

④测试用例4:

结论:与预期一致

⑤测试用例5:

结论:与预期结果一致

⑥测试用例6:

结论:与预期结果一致

(3)博客详情页
测试用例编号 操作 预期结果 1 点击Gitee地址可跳转 跳转到相应Git地址 2 点击编辑按钮 进行更新界面 3 点击删除按钮 可以删除 4 点击右上角注销 用户退出 5 点击右上角主页 回到列表页 6 点击右上角发布新博客 进入编写博客

①测试用例1:

结论:正常跳转与预期一样

②测试用例2:

结论:正常跳转与预期一样

③测试用例3:

结论:正常跳转与预期一样

④测试用例4:

结论:与预期一致

⑤测试用例5:

结论:与预期结果一致

⑥测试用例6:

结论:与预期结果一致

(4)更新博客
测试用例编号 操作 预期结果 1 只编辑标题点击发布 正常发布 2 只编辑内容点击发布 正常发布 3 编辑标题与内容点击发布 正常发布 4 不编辑标题与内容点击发布 正常发布

①测试用例1:

结论:与期望结果一致

②测试用例2:

结论:与期望结果一致

③测试用例3: 

结论:与期望结果一致

④测试用例4: 

结论:与期望结果一致

(5)发布博客
测试用例编号 操作 预期结果 1 只编辑标题点击发布 发布失败 2 只编辑内容点击发布 发布失败 3 编辑标题与内容点击发布 正常发布 4 不编辑标题与内容点击发布 发布失败

①测试用例1: 

结论:发布失败,与期望结果一致。

正文内容是系统默认的,需要正文内容不为空和不是默认的才可以进行发布

②测试用例2: 

结论:与期望结果一致

③测试用例3: 

结论:与期望结果一致

④测试用例4: 

结论:发布失败,与期望结果一致。

正文内容是系统默认的,需要正文内容不为空和不是默认的才可以进行发布

(二)自动化测试(Java)

1.测试环境

系统:windows11

浏览器:Chrome137.0.7151.119

测试工具:IntelliJ IDEA 2025.1.2

2.测试内容
(1)项目结构

(2)工具类Utils
public class Utils { public static WebDriver driver = null; public static WebDriverWait wait = null; public static String blogDetailUrl = \"http://1.94.188.69:9090/blog_detail.html?blogId=3\"; public Utils(String url) { driver = createDriver(); driver.get(url); wait = new WebDriverWait(driver, Duration.ofSeconds(3)); } public static WebDriver createDriver() { if (driver == null) { WebDriverManager.chromedriver().setup(); ChromeOptions options = new ChromeOptions(); Map prefs = new HashMap(); prefs.put(\"credentials_enable_service\", false); prefs.put(\"profile.password_manager_enabled\", false); prefs.put(\"password_manager_leak_detection\", false); prefs.put(\"profile.default_content_settings.popups\", 0); options.setExperimentalOption(\"prefs\", prefs); options.addArguments(\"--remote-allow-origins=*\"); driver = new ChromeDriver(options); driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); } return driver; } public void screenShot(String str) { try { SimpleDateFormat 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 + \"/\" + str + \"_\" + fileTime + \".png\"; File srcFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE); FileUtils.copyFile(srcFile, new File(fileName)); } catch (IOException e) { e.getMessage(); } } public String createTime() { SimpleDateFormat sim = new SimpleDateFormat(\"yyyy-MM-dd-HH-mm-ss-SS\"); String curTime = sim.format(System.currentTimeMillis()); return curTime; } public static void quit() { if (driver != null) { driver.quit(); driver = null; // 重置引用 } }}
(3)登录界面类BlogLoginPage
public class BlogLoginPage extends Utils { public static String url = \"http://1.94.188.69:9090/blog_login.html\"; public BlogLoginPage() { super(url); } public void checkPageRight() { driver.findElement(By.cssSelector(\"body > nav > div > div.flex.items-center.space-x-3\")); driver.findElement(By.cssSelector(\"body > nav > div > div.hidden.md\\\\:flex.items-center.space-x-6 > a:nth-child(1)\")); driver.findElement(By.cssSelector(\"body > nav > div > div.hidden.md\\\\:flex.items-center.space-x-6 > a:nth-child(2)\")); driver.findElement(By.cssSelector(\"#username\")); driver.findElement(By.cssSelector(\"#password\")); driver.findElement(By.cssSelector(\"#submit\")); driver.findElement(By.cssSelector(\"body > div\")); screenShot(\"BlogLoginPage_checkPageRight\"); } public void checkPublishButton() { driver.findElement(By.cssSelector(\"body > nav > div > div.hidden.md\\\\:flex.items-center.space-x-6 > a:nth-child(2)\")).click(); driver.findElement(By.cssSelector(\"#submit\")); screenShot(\"BlogLoginPage_checkPublishButton\"); driver.navigate().back(); } public void checkErrorLogin() { driver.findElement(By.cssSelector(\"#username\")).sendKeys(\"ssssss\"); driver.findElement(By.cssSelector(\"#password\")).sendKeys(\"gssaf\"); driver.findElement(By.cssSelector(\"#submit\")); screenShot(\"BlogLoginPage_checkErrorLogin\"); } public void checkCorrectLogin() { driver.findElement(By.cssSelector(\"#username\")).clear(); driver.findElement(By.cssSelector(\"#password\")).clear(); driver.findElement(By.cssSelector(\"#username\")).sendKeys(\"zhangsan\"); driver.findElement(By.cssSelector(\"#password\")).sendKeys(\"123456\"); screenShot(\"BlogLoginPage_checkCorrectLogin\"); driver.findElement(By.cssSelector(\"#submit\")).click(); driver.findElement(By.cssSelector(\"body > div > div > div.right.flex-1 > div.mb-8.flex.justify-between.items-center > a\")); screenShot(\"BlogLoginPage_checkCorrectLogin\"); }}
(4)博客列表页界面类BlogListPage
public class BlogListPage extends Utils { public static String url = \"http://1.94.188.69:9090/blog_list.html\"; public BlogListPage() { super(url); } public void CheckPageRight() { String title = driver.findElement(By.cssSelector(\"body > div > div > div.right.flex-1 > div.space-y-6.blog-list > div:nth-child(1) > div.title.text-xl.font-bold.text-slate-800.hover\\\\:text-primary.transition-custom.mb-2\")).getText(); String createTiem = driver.findElement(By.cssSelector(\"body > div > div > div.right.flex-1 > div.space-y-6.blog-list > div:nth-child(1) > div.date.text-slate-500.text-sm.flex.items-center.mb-4\")).getText(); String content = driver.findElement(By.cssSelector(\"body > div > div > div.right.flex-1 > div.space-y-6.blog-list > div:nth-child(1) > div.desc.text-slate-600.line-clamp-3.mb-4\")).getText(); String button = driver.findElement(By.cssSelector(\"body > div > div > div.right.flex-1 > div.space-y-6.blog-list > div:nth-child(1) > a\")).getText(); String text = driver.findElement(By.cssSelector(\"body > div > div > div.left.md\\\\:w-1\\\\/4.lg\\\\:w-1\\\\/5 > div > div.px-6.pb-6.-mt-12 > h3\")).getText(); String git = driver.findElement(By.cssSelector(\"body > div > div > div.left.md\\\\:w-1\\\\/4.lg\\\\:w-1\\\\/5 > div > div.px-6.pb-6.-mt-12 > a\")).getText(); driver.findElement(By.cssSelector(\"body > nav > div > div.hidden.md\\\\:flex.items-center.space-x-6 > a.nav-span.text-primary.font-medium.flex.items-center.gap-1\\\\.5\")); driver.findElement(By.cssSelector(\"body > nav > div > div.hidden.md\\\\:flex.items-center.space-x-6 > a.nav-span.text-slate-600.hover\\\\:text-primary.transition-custom.flex.items-center.gap-1\\\\.5\")); driver.findElement(By.cssSelector(\"body > nav > div > div.hidden.md\\\\:flex.items-center.space-x-6 > a.nav-span.text-slate-600.hover\\\\:text-red-500.transition-custom.flex.items-center.gap-1\\\\.5\")); assert !title.isEmpty(); assert !createTiem.isEmpty(); assert !content.isEmpty(); assert !text.isEmpty(); assert !git.isEmpty(); assert button.equals(\"查看全文\"); screenShot(\"BlogListPage_\" + \"CheckPageRight\"); } public void CheckPageGitAndPush() { //检查git按钮 String beforHandle = driver.getWindowHandle(); driver.findElement(By.cssSelector(\"body > div > div > div.left.md\\\\:w-1\\\\/4.lg\\\\:w-1\\\\/5 > div > div.px-6.pb-6.-mt-12 > a\")).click(); String lastHandle = driver.getWindowHandle(); List list = driver.getWindowHandles().stream().toList(); for (String handle : list) { if (!list.equals(beforHandle)) { driver.switchTo().window(handle); } } screenShot(\"BlogListPage_\" + \"CheckPageGitAndPush\"); driver.switchTo(); driver.close(); driver.switchTo().window(beforHandle); driver.findElement(By.cssSelector(\"body > nav > div > div.hidden.md\\\\:flex.items-center.space-x-6 > a:nth-child(2)\")).click(); String title = driver.findElement(By.cssSelector(\"body > div > div > div.mb-6.animate-fade-in > h1\")).getText(); assert title.equals(\"编辑博客\"); driver.navigate().back(); } public void CheackDteail() { String beforTitle = driver.findElement(By.cssSelector(\"body > div > div > div.right.flex-1 > div.space-y-6.blog-list > div:nth-child(1) > div.title.text-xl.font-bold.text-slate-800.hover\\\\:text-primary.transition-custom.mb-2\")).getText(); driver.findElement(By.cssSelector(\"body > div > div > div.right.flex-1 > div.space-y-6.blog-list > div:nth-child(1) > a\")).click(); String lastTitle = driver.findElement(By.cssSelector(\"#blog-content > div.p-6.md\\\\:p-8.border-b.border-slate-100 > h1\")).getText(); assert beforTitle.equals(lastTitle); blogDetailUrl = driver.getCurrentUrl(); driver.findElement(By.cssSelector(\"body > nav > div > div.hidden.md\\\\:flex.items-center.space-x-6 > a:nth-child(1)\")).click(); }}
(5)详情页界面类BlogDetailPage
public class BlogDetailPage extends Utils { public static String url = blogDetailUrl; public BlogDetailPage() { super(url); } public void checkDetailRight() { String buttonEdit = driver.findElement(By.cssSelector(\"#blog-content > div.p-6.md\\\\:p-8.border-t.border-slate-100.bg-slate-50\\\\/50 > div.operating.flex.flex-wrap.gap-4 > button.bg-primary.hover\\\\:bg-primary\\\\/90.text-white.px-5.py-2\\\\.5.rounded-lg.transition-custom.flex.items-center.gap-1\\\\.5.shadow-sm.btn-hover\")).getText(); String title = driver.findElement(By.cssSelector(\"#blog-content > div.p-6.md\\\\:p-8.border-b.border-slate-100 > h1\")).getText(); String text = driver.findElement(By.cssSelector(\"#blog-content > div.p-6.md\\\\:p-8.detail-content\")).getText(); String buttonDelete = driver.findElement(By.cssSelector(\"#blog-content > div.p-6.md\\\\:p-8.border-t.border-slate-100.bg-slate-50\\\\/50 > div.operating.flex.flex-wrap.gap-4 > button.bg-red-500.hover\\\\:bg-red-600.text-white.px-5.py-2\\\\.5.rounded-lg.transition-custom.flex.items-center.gap-1\\\\.5.shadow-sm.btn-hover\")).getText(); driver.findElement(By.cssSelector(\"body > div > div > div.left.md\\\\:w-1\\\\/4.lg\\\\:w-1\\\\/5 > div > div.px-6.pb-6.-mt-12\")); driver.findElement(By.cssSelector(\"body > nav > div > div.hidden.md\\\\:flex.items-center.space-x-6 > a:nth-child(1)\")); driver.findElement(By.cssSelector(\"body > nav > div > div.hidden.md\\\\:flex.items-center.space-x-6 > a:nth-child(2)\")); driver.findElement(By.cssSelector(\"body > nav > div > div.hidden.md\\\\:flex.items-center.space-x-6 > a.nav-span.text-slate-600.hover\\\\:text-red-500.transition-custom.flex.items-center.gap-1\\\\.5\")); assert buttonEdit.equals(\"编辑\"); assert buttonDelete.equals(\"删除\"); assert !title.isEmpty(); assert !text.isEmpty(); driver.findElement(By.cssSelector(\"#blog-content > div.p-6.md\\\\:p-8.border-t.border-slate-100.bg-slate-50\\\\/50 > div.operating.flex.flex-wrap.gap-4 > button.bg-primary.hover\\\\:bg-primary\\\\/90.text-white.px-5.py-2\\\\.5.rounded-lg.transition-custom.flex.items-center.gap-1\\\\.5.shadow-sm.btn-hover\")).click(); String upPageTitle = driver.findElement(By.cssSelector(\"#title\")).getText(); assert title.equals(upPageTitle); } public void DetalUpdatepage() { String beforeTitle=driver.findElement(By.cssSelector(\"#title\")).getText(); //更新 driver.findElement(By.cssSelector(\"#title\")).clear(); driver.findElement(By.cssSelector(\"#title\")).sendKeys(\"自动化更新标题:\" + createTime()); driver.findElement(By.cssSelector(\"#submit\")).click(); String lastTitle = driver.findElement(By.cssSelector(\"body > div > div > div.right.flex-1 > div.space-y-6.blog-list > div:nth-child(1) > div.title.text-xl.font-bold.text-slate-800.hover\\\\:text-primary.transition-custom.mb-2\")).getText(); assert !beforeTitle.equals(lastTitle); } public void DetalDetelepage() { driver.findElement(By.cssSelector(\"body > div > div > div.right.flex-1 > div.space-y-6.blog-list > div:nth-child(1) > a\")).click(); //删除 driver.findElement(By.cssSelector(\"#blog-content > div.p-6.md\\\\:p-8.border-t.border-slate-100.bg-slate-50\\\\/50 > div.operating.flex.flex-wrap.gap-4 > button.bg-red-500.hover\\\\:bg-red-600.text-white.px-5.py-2\\\\.5.rounded-lg.transition-custom.flex.items-center.gap-1\\\\.5.shadow-sm.btn-hover\")).click(); wait.until(ExpectedConditions.alertIsPresent()); Alert alert = driver.switchTo().alert(); alert.accept(); wait.until(ExpectedConditions.alertIsPresent()); alert = driver.switchTo().alert(); alert.accept(); }}
(6)博客编辑类BlogEditPage
public class BlogEditPage extends Utils { public static String url = \"http://1.94.188.69:9090/blog_edit.html\"; public BlogEditPage() { super(url); } public void checkEditPage() { String editNoun = driver.findElement(By.cssSelector(\"body > div > div > div.mb-6.animate-fade-in > h1\")).getText(); driver.findElement(By.cssSelector(\"#submit\")); driver.findElement(By.cssSelector(\"body > nav > div\")); driver.findElement(By.cssSelector(\"body > div > div > div.mb-6.animate-fade-in > p\")); driver.findElement(By.cssSelector(\"#title\")); driver.findElement(By.cssSelector(\"#editor > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div\")); assert editNoun.equals(\"编辑博客\"); //driver.findElement(By.cssSelector(\"#title\")).clear(); } public void EditText() { driver.findElement(By.cssSelector(\"#title\")).sendKeys(\"自动化创建博客标题:\" + createTime()); // 定位 CodeMirror 编辑器元素 WebElement element = driver.findElement(By.cssSelector(\"#editor > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll > div.CodeMirror-sizer > div\")); // 构建输入内容 String content = \"自动化内容生成:\" + createTime() + \"\\n\" + \"自动化内容生成:\" + createTime() + \"\\n\" + \"自动化内容生成:\" + createTime() + \"\\n\" + \"自动化内容生成:\" + createTime(); // 清除现有内容并输入新内容 new Actions(driver) // 移动到编辑器并点击获取焦点 .moveToElement(element) .click() // 全选内容(兼容多平台) .keyDown(System.getProperty(\"os.name\").contains(\"Mac\") ? Keys.COMMAND : Keys.CONTROL) .sendKeys(\"a\") .keyUp(System.getProperty(\"os.name\").contains(\"Mac\") ? Keys.COMMAND : Keys.CONTROL) // 删除选定内容 .sendKeys(Keys.DELETE) // 添加短暂等待确保删除完成 .pause(Duration.ofMillis(200)) // 输入新内容 .sendKeys(content) // 确保输入完成后按回车结束 .sendKeys(Keys.ENTER) .perform(); driver.findElement(By.cssSelector(\"#submit\")).click(); wait.until(ExpectedConditions.alertIsPresent()); Alert alert = driver.switchTo().alert(); alert.accept(); }}
(7)Main函数
public class RunTests { public static void main(String[] args) throws InterruptedException { //登录界面 BlogLoginPage blogLoginPage = new BlogLoginPage(); blogLoginPage.checkPageRight(); //检查界面元素是否加载正常 blogLoginPage.checkPublishButton(); //检查博客按钮是否正常 blogLoginPage.checkErrorLogin(); //账号与密码错误时 blogLoginPage.checkCorrectLogin(); //账号与密码正确时 //博客列表界面 BlogListPage blogListPage = new BlogListPage(); blogListPage.CheckPageRight(); //检查界面元素是否加载正常 blogListPage.CheckPageGitAndPush();//检查git与发布按钮是否可以跳转 blogListPage.CheackDteail(); //进入详情页 //博客更新/删除 BlogDetailPage blogDetailPage = new BlogDetailPage(); blogDetailPage.checkDetailRight(); blogDetailPage.DetalUpdatepage(); blogDetailPage.DetalDetelepage(); //编辑博客 BlogEditPage blogEditPage = new BlogEditPage(); blogEditPage.checkEditPage(); blogEditPage.EditText(); Utils.quit(); }}

(三)性能测试 

使用JMeter工具对论坛系统的接口进行简单的性能测试:

1.添加HTTP相关接口信息添加以及HTTP头文件管理器

   (1)登录接口测试

以JSON格式发送

添加CSV文件,可以保证多个用户轮流登录

(2)博客列表接口测试

添加基本接口信息

写的token进行请求接口

配置JSON提取器,方便其他接口访问当前信息

(3)博客详情页接口测试

(4)更新博客接口测试

(5)发布博客接口

(6)删除接口测试

2.添加同步定时器

3.压力性能测试报告

一、核心指标概览

        1、测试基础信息

测试脚本:test.jmx

持续时间:仅1分钟(11:54 PM → 11:55 PM),时长过短可能导致数据偶然性。

总请求量:1974次(平均并发≈33请求/秒)

        2、成功率与失败率

通过率:99.8%(1970次成功)

失败率:0.2%(4次失败)

二、深度问题诊断

        1. 错误分析(致命问题)

错误类型

次数

影响接口

根本原因

java.net.SocketException

3

登录接口

​​TCP连接超时​​

HttpHostConnectException

1

登录接口

目标服务器无响应

错误细节:

Connect to 1.94.188.69:9090 failed: Connection timed out

问题定位:

目标服务器 1.94.188.69:9090 的网络连通性故障或端口阻塞

服务器过载未及时响应(登录接口响应中位数达 18.4秒!)

        2. 登录接口性能瓶颈

指标

登录接口

其他接口(对比)

请求次数

343

≈325次/接口

错误次数

​​3​​

≤1

响应中位数

​​18415ms​​

约2ms~5ms

P95响应时间

​​3388ms​​

<10ms

结论:

登录接口存在严重性能缺陷(响应延迟高达18秒)

错误率0.87%(远超其他接口0%~0.31%),是系统瓶颈点

        3. Apdex指数分析

全局Apdex:0.879(尚可接受,但局部异常)

登录接口Apdex:0.542(远低于均值)

用户对登录接口的体验满意度显著劣化(500ms容忍阈值下)

以上为本次报告全部内容,感谢观看。