🌈個人主頁: Aileen_0v0
🔥熱門專欄: 華為鴻蒙系統學習|計算機網絡|數據結構與算法
?💫個人格言:“沒有羅馬,那就自己創造羅馬~”
文章目錄
- 項目背景:
- 項目背景與意義:
- 項目概述
- 已實現的主要功能包括:
- 當前系統存在的不足:
- 項目的系統功能說明
- 1. 登錄功能
- 2. 博客列表頁
- 3. 博客詳情頁
- 4. 博客編輯頁
- 測試目標:
- 測試項目相關信息:
- 測試安排:
- 測試分類:
- (1)功能測試
- (2)自動化測試
- ①編寫Web測試用例:
- ②創建空項目
- (1)配置準備:添加需要的pom.xml依賴
- (2)創建驅動對象driver
- (3)測試用例的劃分
- 自動化測試工具準備:
- 測試用例tests的編寫:
- ①登錄頁面:
- 檢查頁面的正常加載:
- 檢查成功登錄:
- 檢查登錄失敗:
- ②列表頁面測試:
- ③編輯頁面:
- ④:詳情頁面:
- (3)性能測試:
- 模擬并發訪問登錄頁面:
- 總結測試 tips:
項目名稱 | 博文匯 | 版本號 | / |
---|---|---|---|
發布類型 | 分級發布 | 測試負責人 | Aileen |
測試完成日期 | 7/9 | 聯系方式 |
項目背景:
項目背景與意義:
- 隨著互聯網的發展,博客平臺成為技術交流、知識分享的重要載體。CSDN作為國內知名的技術社區,為廣大開發者提供了豐富的學習與交流資源。作為一名CSDN博主,我在日常寫作和交流中深刻體會到一個高效、便捷、安全的博客系統對于個人成長和技術傳播的重要性。因此,我以CSDN為參考,設計并開發了**“博文匯”**項目,旨在打造一個簡潔易用、功能完善的個人博客平臺,便于回顧所學的知識。
- 本項目的創建不僅是對主流博客平臺功能的實踐和復現,更是對Web開發全流程的系統學習和能力提升。通過項目開發,我深入掌握了Java、Spring Boot、前端技術、JWT 令牌加密等關鍵技能,并在實際應用中強化了對安全性、用戶體驗、系統可維護性的理解,實現將自己所學的知識融會貫通到項目中,加深自己對技術的理解。
- 若有不足或改進之處,也希望大家指出??~
項目概述
本項目實現了一個基于前后端分離架構的個人博客系統,采用數據庫存儲用戶與博客數據,并部署在云服務器上。系統前端主要由四個頁面組成:登錄頁、博客列表頁、博客詳情頁和博客編輯頁,整體模擬實現了一個簡潔的個人博客平臺。
已實現的主要功能包括:
- 用戶登錄與注銷
- 博客的發布與刪除
- 博客列表與詳情查看
- 強制登錄驗證(未登錄狀態下無法訪問其他頁面)
當前系統存在的不足:
- 未實現用戶注冊功能,用戶信息需提前寫入數據庫
- 用戶頭像為靜態圖片,無法自定義設置
- 用戶文章數與分類數未在后端動態統計,前端為靜態展示
項目的系統功能說明
1. 登錄功能
- 用戶需輸入已存在于數據庫中的用戶名和密碼進行登錄
- 登錄成功后跳轉至博客列表頁
- 未登錄狀態下點擊“主頁”或“寫博客”按鈕,將強制跳轉至登錄頁
2. 博客列表頁
- 展示博客的簡要信息,包括標題、發布時間、內容概要
- 左側顯示當前登錄用戶的基本信息(如用戶名、文章數、分類數等)
- 右上角提供三個功能按鈕:
- 主頁:返回博客列表頁
- 寫博客:跳轉至博客編輯頁
- 注銷:退出當前用戶,返回登錄頁
3. 博客詳情頁
- 點擊列表頁中的“查看全文”按鈕進入詳情頁
- 展示博客的完整內容,包括標題、發布時間、作者及正文
- 右上角提供四個功能按鈕:
- 主頁:返回博客列表頁
- 寫博客:跳轉至博客編輯頁
- 刪除:刪除當前博客,刪除后返回列表頁
- 注銷:退出當前用戶,返回登錄頁
4. 博客編輯頁
- 登錄狀態下點擊“寫博客”按鈕進入編輯頁
- 用戶可輸入博客標題與正文內容
- 點擊“發布文章”按鈕后,博客將被保存并發布,隨后跳轉至博客列表頁
測試目標:
- 目標:主頁面測試率達到 90%
測試項目相關信息:
- 項目鏈接地址:http://47.94.167.83:8080/blog_login.html
- 項目代碼:https://gitee.com/gu-ling-c-language/spring_-blog
- 接口:登錄,主頁,編輯,詳情頁
測試安排:
模塊 | 子模塊 | 前端 | 開發 | 提測時間 | 測試 | 工時 | 排期 | 進度 | 備注 |
---|---|---|---|---|---|---|---|---|---|
登陸 | 登陸功能 | Aileen | Aileen | 6.30 | Aileen | 0.5d | 6.31 | 測試完成 | |
主頁 | 博主信息、博客列表頁、導航欄 | Aileen | Aileen | 6.30 | Aileen | 0.5d | 6.31 | 測試完成 | |
寫博客 | 博客編輯與發布 | Aileen | Aileen | 6.31 | Aileen | 1d | 7.3 | 測試完成 | |
博客詳情頁 | 博客詳情頁 | Aileen | Aileen | 6.31 | Aileen | 0.5d | 7.3 | 測試完成 |
測試分類:
(1)功能測試
我的博文項目測試
- 功能測試結果:測試用例 100%通過
(2)自動化測試
①編寫Web測試用例:
②創建空項目
(1)配置準備:添加需要的pom.xml依賴
<dependency><groupId>io.github.bonigarcia</groupId><artifactId>webdrivermanager</artifactId><version>5.8.0</version><scope>test</scope>
</dependency><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>
(2)創建驅動對象driver
- 為了方便測試所有的頁面,我們創建一個driver對象供所有的測試用例使用
(3)測試用例的劃分
- 按照頁面分類,每個頁面就是一個Java文件,頁面下所有的用例統一管理
自動化測試工具準備:
package common;import io.github.bonigarcia.wdm.WebDriverManager;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;public class Utils {//創建WebDriver對象public static WebDriver driver;public static WebDriver createDriver(){//先判斷驅動是否為nullif(driver == null) {//如果為null,則創建一個新的WebDriver實例WebDriverManager.chromedriver().setup();ChromeOptions options = new ChromeOptions();// 允許訪問所有網站options.addArguments("--allow-remote-origins=*");//創建ChromeDriver實例driver = new ChromeDriver(options);//等待->隱式等待driver.manage().timeouts().implicitlyWait(java.time.Duration.ofSeconds(2));}return driver;}public Utils(String url){//調用 driver 對象driver = createDriver();//訪問urldriver.get(url);}//測試截圖public void getScreenShot(String str) throws IOException {//截圖放置的位置: ./src/test/image/// /2025-7-20/// /test01-150001.png// /test02-150030.png// /2025-7-21/// /test01-150001.png// /test02-150030.png//格式設置SimpleDateFormat sim1 = new SimpleDateFormat("yyyy-MM-dd");SimpleDateFormat sim2 = new SimpleDateFormat("HHmmssSS");//SS將文件名精確到毫秒級別避免截圖同名String dirTime = sim1.format(System.currentTimeMillis());String fileTime = sim2.format(System.currentTimeMillis());// ./src/test/image/2025-7-20/test01-150001.pngString filename = "./src/test/image/"+ dirTime +"/"+str +"-"+ fileTime+".png";//打印一下生成的filename名稱System.out.println("filename: "+ filename);//屏幕截圖File srcFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);//srcFile放到指定位置FileUtils.copyFile(srcFile , new File(filename));}}
- 后面測試用例就可以通過繼承這個工具創建測試對象,訪問URL
測試用例tests的編寫:
①登錄頁面:
- 登錄頁面的基本功能測試:只有賬號和密碼匹配才能正常登錄,其他情況都登錄失敗。
檢查頁面的正常加載:
package tests;import common.Utils;
import org.openqa.selenium.By;public class LoginPage extends Utils {public static String url = "http://47.94.167.83:8080/blog_login.html";/*** 構造函數,調用父類Utils的構造函數* 訪問登錄頁面*/public LoginPage() {super(url);}/***檢查頁面是否能正確加載* @throws InterruptedException*/public void loginPageRight() throws InterruptedException {//查看頁面元素是否存在來檢查頁面是否能成功加載//這里選擇主頁按鈕元素driver.findElement(By.cssSelector("body > div.nav > a:nth-child(4)"));//登錄輸入框driver.findElement(By.cssSelector("body > div.container-login > div"));Thread.sleep(3000);driver.quit();}
}
檢查成功登錄:
/*** 檢查登錄功能 - 成功登錄*/public void LoginSuc(){//找到登錄頁面中的用戶輸入框,輸入賬號和密碼并點擊登錄按鈕driver.findElement(By.cssSelector("#username")).sendKeys("Aileen");driver.findElement(By.cssSelector("#password")).sendKeys("123456");driver.findElement(By.cssSelector("#submit")).click();//檢查登錄之后是否登陸成功//檢查頁面是否跳轉到博客列表頁//根據博客列表頁的元素來判斷driver.findElement(By.cssSelector("body > div.container > div.right > div:nth-child(1) > a"));//檢查頁面標題是否正確--這里需要配置斷言String exceptTitle = driver.getTitle();assert exceptTitle.equals("博客列表頁") : "登錄失敗,頁面標題不正確!";driver.quit();}
檢查登錄失敗:
- 當我們登錄成功之后通過 navigate.back() 回到登錄頁之后,需要先將賬號密碼清空才能進行登錄失敗的測試,否則會疊加到原來成功登錄輸入的內容之后:
/*** 檢查登錄功能 - 登錄失敗*///檢查登錄失敗,在測完登錄成功之后需要先回到登錄頁面才能進行登錄失敗的檢查↑public void LoginFail() throws InterruptedException {//先清空之前成功登錄在輸入框中輸入的內容://清空方式有兩種://1.使用clear()方法driver.findElement(By.cssSelector("#username")).clear();driver.findElement(By.cssSelector("#password")).clear();//2.使用refresh()方法刷新頁面//driver.navigate().refresh();//輸入錯誤的賬號和密碼driver.findElement(By.cssSelector("#username")).sendKeys("Aileen111");//賬號錯誤driver.findElement(By.cssSelector("#password")).sendKeys("123456");driver.findElement(By.cssSelector("#submit")).click();//等待alert彈窗出現Thread.sleep(2000); // 等待2秒鐘,確保alert彈窗出現//獲取失敗的彈窗內容,將它和預期的錯誤信息進行比較String alertText = driver.switchTo().alert().getText();String expectedAlertText = "用戶不存在";assert alertText.equals(expectedAlertText);//接受彈窗driver.switchTo().alert().accept();driver.quit();}
②列表頁面測試:
- 測試列表頁面又分為兩種情況:登錄成功和登錄失敗(未登錄狀態)去訪問博客列表頁:
package tests;import common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;import java.io.IOException;public class ListPage extends Utils {public static String url = "http://127.0.0.1:8080/blog_list.html";public ListPage() {super(url);}/*** 根據登錄狀態訪問列表頁* 這里需要先登錄成功* 測試看列表頁是否有只屬于列表頁的元素* 有則說明訪問成功*/public void ListByLogin(){//查看是否能加載列表頁String exceptTitle = driver.getTitle();assert exceptTitle.equals("博客列表頁") : "列表頁加載失敗,頁面標題不正確!";//查看頁面元素是否存在來檢查頁面是否能成功加載//這里選擇查看全文按鈕WebElement ele = driver.findElement(By.cssSelector("body > div.container > div.right > div:nth-child(1) > a"));//通過斷言檢查是否有這個按鈕assert ele.isDisplayed() : "列表頁加載失敗,頁面元素不存在!";//點擊查看全文按鈕ele.click();}
}
- 未登錄狀態查看博客列表處理彈窗出現問題
- 我通過給每個測試用例屏幕截圖查找問題
- 通過截圖我們可以看到:我們執行到我們本來預期是:以未登錄狀態訪問列表頁的,但是當它執行并跳轉到未登錄訪問博客列表的測試方法前,它通過 driver 訪問博客列表時,還保存著之前的登錄狀態,所以未登錄的方法的警告框實際上并未出現。
③編輯頁面:
package tests;import common.Utils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;/*** 特殊測試:編輯頁面* 由于markdown是第三方庫,無法直接測試博客的發布功能,但是默認情況下頁面編輯已有默認內容,* 但是我們可以測試編輯頁面的標題,因為標題是我自己實現的HTML元素*/
public class EditPage extends Utils {public static String url = "http://127.0.0.1:8080/blog_edit.html";public EditPage() {super(url);}/*** 發布博客測試* 無法輸入博客內容怎么解決----->兩種方式*/public void EditSuc() throws InterruptedException {//①方式1:博客本身就有默認內容,無需手動實現WebElement ele = driver.findElement(By.cssSelector("#title"));Thread.sleep(6000); //等待2秒鐘,查看輸入的效果ele.sendKeys("烏薩奇的博客");Thread.sleep(2000); //等待2秒鐘,查看輸入的效果driver.findElement(By.cssSelector("#submit")).click();//------------//點擊彈窗的確認按鈕->跳轉到博客列表頁Thread.sleep(1000);driver.switchTo().alert().accept();//②方式2:通過鼠標操作來實現---這里就不實現了,有點麻煩...//檢查博客發布之后是否成功在列表頁展示出WebElement exceptArticleTitle = driver.findElement(By.cssSelector("body > div.container > div.right > div:nth-child(1) > div.title"));String exceptTitle = exceptArticleTitle.getText();assert exceptTitle.equals("烏薩奇的博客") : "編輯頁面發布博客失敗,標題不正確!";}
}
④:詳情頁面:
package tests;import common.Utils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;public class DetailPage extends Utils {public static String url = "http://127.0.0.1:8080/blog_detail.html?blogId=14";public DetailPage() {super(url);}/*** 檢查詳情頁是否能正確加載* 通過查找博客標題元素來判斷* @throws InterruptedException*/public void DetailByLogin() throws InterruptedException {//查看頁面元素是否存在來檢查頁面是否能成功加載//這里選擇編輯按鈕//用戶點擊編輯,然后修改標題,點擊發布,跳轉到博客列表頁String exceptDetailTitle = driver.getTitle();assert exceptDetailTitle.equals("博客詳情頁") : "詳情頁加載失敗,頁面標題不正確!";Thread.sleep(3000);//點擊編輯按鈕driver.findElement(By.cssSelector("body > div.container > div.right > div > div.operating > button:nth-child(1)")).click();Thread.sleep(3000);//修改標題WebElement ele = driver.findElement(By.cssSelector("#title"));Thread.sleep(2000); //等待2秒鐘,查看輸入的效果ele.clear();Thread.sleep(2000); //等待2秒鐘,查看輸入的效果ele.sendKeys("永不言棄的Aileen");Thread.sleep(2000); //等待2秒鐘,查看輸入的效果driver.findElement(By.cssSelector("#submit")).click();//將內容發表->點擊發布成功彈窗的按鈕Thread.sleep(2000);driver.switchTo().alert().accept(); //點擊彈窗的確認按鈕->跳轉到博客列表頁Thread.sleep(2000);// 跳轉到博客列表頁//檢查是否跳轉到博客列表頁String exceptTitle = driver.getTitle();assert exceptTitle.equals("博客列表頁") : "詳情頁修改失敗,無法跳轉到博客列表頁!";//注銷按鈕測試//點擊注銷按鈕driver.findElement(By.cssSelector("body > div.nav > a:nth-child(6)")).click();Thread.sleep(3000);//檢查是否能跳轉到登錄頁面String exceptLoginTitle = driver.getTitle();assert exceptLoginTitle.equals("博客登陸頁") : "注銷失敗,無法跳轉到登錄頁面!";}
}
(3)性能測試:
模擬并發訪問登錄頁面:
- 2s 內 10 個虛擬用戶不斷發送請求,總共發送了 4445 次請求
- 我們可以看到響應時間和吞吐量成反比:
- 響應時間長,吞吐量小或相對穩定,可能系統達到了性能瓶頸。
- 響應時間短,吞吐量大,性能越好
- 根據線程響應時間圖,我們可以看到,存在一個異常:有一個線程一直沒有退出,下面我們結合聚合報告進一步分析:
- 我們可以考到列表頁的響應時間是最長的,說明可能是列表頁存在問題,我覺得是因為我們每次列表頁返回的都是所有的數據,響應量大,數據較多,導致時間較長
- **解決方案:將返回的響應量分解,給博客列表頁添加分頁,限制每頁返回的最大博客條數,這樣就可以有效解決響應時間過長的問題。**
- 通過 JMeter 生成測試報告,可看到總共 959 個請求,沒有錯誤,錯誤率為 0,說明高并發場景下,系統能夠正常處理業務。
- 但是博客列表頁的響應時間太長了,應該是一次請求的響應資源太多了,我們可以將一次請求的請求量分多次請求來減少響應時間,通過給列表頁添加翻頁功能,限制每頁展示的博客條數
總結測試 tips:
- 先測試部分接口功能是否能正常實現,再進行整體測試,要注意各個接口之間的關聯關系。