【博文汇项目全维度测试报告:功能与自动化双轨验证】
🌈个人主页: 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:
- 先测试部分接口功能是否能正常实现,再进行整体测试,要注意各个接口之间的关联关系。