一、項目簡介
? ? ? ?思緒網作為一種在線交流平臺,支持用戶在平臺下發布文章,并進行討論。主要由登錄頁面,論壇頁面,帖子編輯頁,帖子詳情頁等頁面組成。
二、項目功能
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<String, Object> 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<String> 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容忍閾值下)
以上為本次報告全部內容,感謝觀看。