一.项目背景
这是一个基于Spring+前后端分离的网络聊天室项目 ,是一款集用户管理、信息同步发送于一体的简单聊天类应用
1.项目介绍
模块设计
-
用户模块:实现用户登录、注册基本功能,保障用户身份管理与信息安全
-
会话模块:支持会话添加、管理、列表展示及发送消息互动,满足用户的互动需求。
-
好友列表功能:实现管理好友功能,便于查找好友进行聊天
-
搜索功能:支持向指定用户添加好友,让用户"找得到、加得准、用得顺"
-
**发送消息:**用websocket同步进行消息发送,实时更新最新状态
模拟服务器有效地将消息推送到客户端(或浏览器)
技术栈
**•**后端:基于 Spring Boot + Spring MVC 构建服务端逻辑,以 MySQL 作为数据存储引擎,保障系统稳定性与数据可靠性。
**•**前端:通过简单的html实现响应式页面布局,结合 jQuery 完成 DOM 操作与 AJAX 异步请求,确保前端交互体验流畅直观
2.技术应用
-
后端:Spring,Spring Boot,Spring MVC,MyBatis
-
数据库:MySQL
-
前端:HTML,CSS,JavaScript,jQuery3
• 采用统一返回格式 + 全局错误信息定义,实现前后端交互结果的标准化、一致性处理,提升接口可读性与稳定性
• 通过 @ControllerAdvice + @ExceptionHandler 注解组合,构建全局异常处理机制,统一拦截、规范化异常响应
• 利用拦截器技术实现用户登录状态校验,精准拦截未授权请求,强化系统权限管理与操作安全性
• 采用jwt令牌进行身份验证,使用无状态token进行身份校验,避免了session占用服务器资源以及CSRF攻击
二.项目模块及其实现功能
1.模块功能
1>.注册模块
2>.登陆模块
3>.板块模块
• 标签之间的切换及其获取对应的信息
(eg:点击会话加载会话列表、点击好友加载对应的好友列表、点击好友申请加载申请列表)
• 实现滑动切换标签的交互,提升操作流畅度
4>.添加好友模块
• 输入搜索内容,获取对应的好友信息
• 展示搜索结果列表,包含用户头像、昵称、个性签名等基础信息
• 在好友申请点击加载内容,进行同意好友、拒绝好友实现
5>:发送信息
2.测试用例

三.编写自动化测试完成功能测试
1.包的结构与公共代码(Common)

java
package chatroom.Common;
import chatroom.Exception.TestException;
import chatroom.TestApi.Utils.Utils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class Common {
/**
* 会话页面
*/
public static void SessionNorDisplay(WebDriver driver){
System.out.println("------------------------------------------------");
// 点击一下会话标签
driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-session")).click();
// 获取里面的li标签
WebElement sessionList = driver.findElement(By.cssSelector("#session-list"));
List<WebElement> li = sessionList.findElements(By.tagName("li"));
if(li.size() > 0){
// 此时随便看一下元素显示
String text = li.get(0).findElement(By.cssSelector("h3")).getText();
System.out.println("会话框第一个名称>:"+text);
}else if(li.size() == 0){
// 看一下元素
String h3 = sessionList.findElement(By.cssSelector("h3")).getText();
assert h3.equals("当前没有聊天记录") : "会话框没有添加好友时描述错误,"+h3;
}else throw new TestException("会话页面显示错误");
System.out.println("会话框元素显示正常");
}
/**
* 好有列表页面
*/
public static void FriendListNorDisplay(WebDriver driver){
System.out.println("------------------------------------------------");
// 点击一下好友列表
driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-friend")).click();
// 获取里面的li标签
WebElement sessionList = driver.findElement(By.cssSelector("#friend-list"));
List<WebElement> li = sessionList.findElements(By.tagName("li"));
if(li.size() > 0){
// 此时随便看一下元素显示
String text = li.get(0).findElement(By.cssSelector("h4")).getText();
System.out.println("好友列表第一个名称>:"+text);
}else if(li.size() == 0){
// 看一下元素
String h3 = sessionList.findElement(By.cssSelector("h3")).getText();
assert h3.equals("当前没有好友,请添加好友") : "好友列表没有添加好友时描述错误,"+h3;
}else throw new TestException("好友列表页面显示错误");
System.out.println("好友列表元素显示正常");
}
/**
* 好友申请页面
*/
public static void ApplyNorDisplay(WebDriver driver){
System.out.println("------------------------------------------------");
// 点击一下好友申请标签
driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-request")).click();
// 获取里面的li标签
WebElement sessionList = driver.findElement(By.cssSelector("#add_firend_list"));
List<WebElement> li = sessionList.findElements(By.tagName("li"));
if(li.size() > 0){
// 此时随便看一下元素显示
String content = li.get(0).findElement(By.cssSelector("#friend_left > h3")).getText().trim();
String text = content.split(" ")[0];
System.out.println("好友申请第一个名称?:"+text);
}else if(li.size() == 0){
// 看一下元素
String h3 = sessionList.findElement(By.cssSelector("h3")).getText();
assert h3.equals("当前没有好友请求") : "好求请求描述错误,"+h3;
}else throw new TestException("好友请求页面显示错误");
System.out.println("好友申请元素显示正常");
}
/**
* 全屏截图
* @param str
*/
public static void FullScreenCapture(String str){
try {
// 1. 创建Robot对象,用于操作屏幕
Robot robot = new Robot();
// 2. 获取屏幕尺寸(全屏大小)
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Rectangle screenRectangle = new Rectangle(screenSize);
// 3. 执行全屏截图
BufferedImage screenImage = robot.createScreenCapture(screenRectangle);
// 4. 保存图片到本地(格式支持:png、jpg、gif)
String path = new Utils().path(str);
File outputFile = new File(path);
if(!outputFile.getParentFile().isDirectory())outputFile.mkdirs();
ImageIO.write(screenImage, "png", outputFile);
//
// System.out.println("全屏截屏成功!图片保存路径:" + outputFile.getAbsolutePath());
} catch (AWTException e) {
System.err.println("截屏工具初始化失败:" + e.getMessage());
} catch (IOException e) {
System.err.println("图片保存失败:" + e.getMessage());
}
}
}
2.登录接口测试代码
java
package chatroom.TestApi.Login;
import chatroom.TestApi.Utils.Utils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.io.IOException;
import java.time.Duration;
public class Login extends Utils {
private static final String url = "http://127.0.0.1:9091/login.html";
public Login() {
super(url);
}
public void verificationIsLogin(){
// 进行校验
webDriver.findElement(By.cssSelector("body > div.login-container > div > h3"));
webDriver.findElement(By.cssSelector("#submit"));
System.out.println("登录页面正常打开");
}
public void NormalLogin(String username,String password){
webDriver.findElement(By.cssSelector("#username")).clear();
webDriver.findElement(By.cssSelector("#password")).clear();
webDriver.findElement(By.cssSelector("#username")).sendKeys(username);
webDriver.findElement(By.cssSelector("#password")).sendKeys(password);
webDriver.findElement(By.cssSelector("#submit")).click();
// 进行校验
WebDriverWait wait = new WebDriverWait(webDriver, 3);
wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-session")));
wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-friend")));
}
/**
* 账号为空
*/
public void AccountIsNullLogin(String password) throws InterruptedException, IOException {
webDriver.findElement(By.cssSelector("#username")).clear();
webDriver.findElement(By.cssSelector("#password")).clear();
webDriver.findElement(By.cssSelector("#password")).sendKeys(password);
webDriver.findElement(By.cssSelector("#submit")).click();
Thread.sleep(500);
WebElement popup = webDriver.findElement(By.cssSelector("body > div.jq-toast-wrap.bottom-left > div > h2"));
assert popup.getText().equals("警告") : "用户名为空时提示错误";
getScreenShot("用户名为null参数校验失败-"+getCurrentMothed());
webDriver.findElement(By.cssSelector("body > div.jq-toast-wrap.bottom-left > div > span.close-jq-toast-single")).click();
}
/**
* 账号错误
*/
public void AccountIsFailLogin(String fakeUsername,String password) throws InterruptedException, IOException {
Thread.sleep(200);
webDriver.findElement(By.cssSelector("#username")).clear();
webDriver.findElement(By.cssSelector("#username")).sendKeys(fakeUsername);
webDriver.findElement(By.cssSelector("#password")).clear();
webDriver.findElement(By.cssSelector("#password")).sendKeys(password);
webDriver.findElement(By.cssSelector("#submit")).click();
Thread.sleep(500);
WebElement popup = webDriver.findElement(By.cssSelector("body > div.jq-toast-wrap.bottom-left > div > h2"));
assert popup.getText().equals("警告") : "用户名为空时提示错误";
getScreenShot("用户名错误参数校验失败-"+getCurrentMothed());
webDriver.findElement(By.cssSelector("body > div.jq-toast-wrap.bottom-left > div > span.close-jq-toast-single")).click();
}
/**
* 密码为空
*/
public void PasswordIsNUllFailLogin(String username) throws InterruptedException, IOException {
Thread.sleep(200);
webDriver.findElement(By.cssSelector("#username")).clear();
webDriver.findElement(By.cssSelector("#username")).sendKeys(username);
webDriver.findElement(By.cssSelector("#password")).clear();
webDriver.findElement(By.cssSelector("#submit")).click();
Thread.sleep(500);
WebElement popup = webDriver.findElement(By.cssSelector("body > div.jq-toast-wrap.bottom-left > div > h2"));
assert popup.getText().equals("警告") : "用户名为空时提示错误";
getScreenShot("密码为空校验失败-"+getCurrentMothed());
webDriver.findElement(By.cssSelector("body > div.jq-toast-wrap.bottom-left > div > span.close-jq-toast-single")).click();
}
/**
* 密码错误
*/
public void PasswordIsFailLogin(String username,String fakePassword) throws InterruptedException, IOException {
Thread.sleep(200);
webDriver.findElement(By.cssSelector("#username")).clear();
webDriver.findElement(By.cssSelector("#username")).sendKeys(username);
webDriver.findElement(By.cssSelector("#password")).clear();
webDriver.findElement(By.cssSelector("#password")).sendKeys(fakePassword);
webDriver.findElement(By.cssSelector("#submit")).click();
Thread.sleep(500);
WebElement popup = webDriver.findElement(By.cssSelector("body > div.jq-toast-wrap.bottom-left > div > h2"));
assert popup.getText().equals("警告") : "用户名为空时提示错误";
getScreenShot("密码错误校验失败-"+getCurrentMothed());
webDriver.findElement(By.cssSelector("body > div.jq-toast-wrap.bottom-left > div > span.close-jq-toast-single")).click();
}
}
3.注册功能测试代码
java
package chatroom.TestApi.Model;
public class RegisterModel {
private String username;
private String nickname;
private String password;
private String repeatedPassword;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRepeatedPassword() {
return repeatedPassword;
}
public void setRepeatedPassword(String repeatedPassword) {
this.repeatedPassword = repeatedPassword;
}
}
4.首页功能测试代码
java
package chatroom.TestApi.HomePage;
import chatroom.Common.Common;
import chatroom.TestApi.Utils.Utils;
import org.openqa.selenium.By;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;
public class HomePage extends Utils {
private static final String URL = "http://127.0.0.1:9091/login.html";
private static final String LOGIN_URL = "http://127.0.0.1:9091/client.html";
public HomePage() {
super(LOGIN_URL);
}
/**
* 直接进入首页,提示登录失败,并跳转到登录页面
*/
public void notEnterClientPage(){
System.out.println("当前的首页的url>:"+webDriver.getCurrentUrl());
WebDriverWait wait = new WebDriverWait(webDriver, 5);
wait.until(ExpectedConditions.urlContains("login.html"));
String currentUrl = webDriver.getCurrentUrl();
System.out.println("跳转后的url页面>:"+currentUrl);
// 判断是不是sign-in.html
assert currentUrl.equals(LOGIN_URL) : "未登录时的页面跳转失败,跳转的url是"+currentUrl;
System.out.println("未登录时的页面跳转成功");
}
/**
* 登录进去检查信息
*/
public void sucEnterClientPage(){
login("zhangsan","123456");
WebDriverWait wait = new WebDriverWait(webDriver, 5);
wait.until(ExpectedConditions.urlContains("client.html"));
String currentUrl = webDriver.getCurrentUrl();
assert currentUrl.equals(LOGIN_URL) : "未登录时的页面跳转失败,跳转的url是"+currentUrl;
// 检查界面元素
wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-session")));
wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-friend")));
}
/**
* 普通的web
*/
public void userWebDriver(){
Common.SessionNorDisplay(webDriver);
Common.FriendListNorDisplay(webDriver);
Common.ApplyNorDisplay(webDriver);
}
/**
* 无痕浏览器
*/
public void userTraceDriver() throws InterruptedException {
// 先登录
TraceLogin("testName","123456",URL,false);
Thread.sleep(500);
Common.SessionNorDisplay(traceless);
Common.FriendListNorDisplay(traceless);
Common.ApplyNorDisplay(traceless);
}
}
5.添加好友功能测试代码
java
package chatroom.TestApi.FriendApply;
import chatroom.Common.Common;
import chatroom.TestApi.Send.SendMessage;
import chatroom.TestApi.Utils.Utils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import javax.swing.text.TabExpander;
import java.io.IOException;
import java.time.Duration;
import static chatroom.Main.sendMessage;
public class FriendApply extends Utils {
private static final String URL = "http://127.0.0.1:9091/login.html";
public FriendApply() {
super(URL);
}
public void applyFriend() throws InterruptedException, IOException {
// 先打开两个浏览器
// 接着左右分屏
login("zhangsan","123456");
setLeftWindow(webDriver);
TraceLogin("testName","123456",URL,true);
// 1.开始时需要截图双方的会话 好友 好友申请
getAllScreenShot();
// 2.张三发送好友申请,testName 拒绝,此时先截图,再点击拒绝
String searchContent = "testName";
String applyContent = "你好,testName想认识一下你~~";
sendApply(searchContent,applyContent);
rejectedApply();
Thread.sleep(2000);
// 3.张三重新发送好友申请,testName 接受,此时 进行一次消息发送
sendApply(searchContent,applyContent);
Thread.sleep(1000);
AgreeApply();
}
/**
* 同意申请
*/
private void AgreeApply() throws InterruptedException {
traceless.navigate().refresh();
Thread.sleep(2000);
traceless.findElement(By.xpath("/html/body/div[2]/div/div[1]/div[3]/div[3]")).click();
Thread.sleep(100);
traceless.findElement(By.cssSelector("#button_right > button.agree")).click();
// 获取弹窗
String text = traceless.findElement(By.cssSelector("body > div.jq-toast-wrap.bottom-left > div > h2")).getText();
assert text.equals("成功") : "点击添加好友,显示错误";
Thread.sleep(250);
Common.FullScreenCapture("添加好友后的会话列表");
// 聊天
SendMessage.reallySendMessage(webDriver,traceless,true,4);
// 点击好友列表查看
webDriver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-friend")).click();
traceless.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-friend")).click();
Common.FullScreenCapture("添加好友后的好友列表");
}
/**
* 拒绝申请
*/
private void rejectedApply() throws InterruptedException {
traceless.navigate().refresh();
Thread.sleep(2000);
traceless.findElement(By.xpath("/html/body/div[2]/div/div[1]/div[3]/div[3]")).click();
Thread.sleep(100);
traceless.findElement(By.cssSelector("#button_right > button.reject")).click();
WebElement popup = traceless.findElement(By.cssSelector("body > div.jq-toast-wrap.bottom-left > div"));
String text = popup.findElement(By.cssSelector("div > h2")).getText();
assert text.equals("成功") : "点击好友申请拒绝按钮后,内容显示错误";
// 截图
Common.FullScreenCapture("好友申请拒绝后截图");
WebDriverWait wait = new WebDriverWait(traceless, 15);
wait.until(ExpectedConditions.invisibilityOf(popup));
}
/**
* 搜索对应的好友并发送请求
* @param searchContent 搜索框内容
* @param applyContent 申请理由
*/
private void sendApply(String searchContent,String applyContent) throws InterruptedException {
WebElement search = webDriver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.search > input[type=text]"));
search.sendKeys(searchContent);
webDriver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.search > button")).click();
// 检查元素是否清空
String text = search.getText();
assert text.length() == 0 && text.equals("") : "输入框没有清空";
// 输入申请理由
WebElement element = webDriver.findElement(By.cssSelector("body > div.client-container > div > div.right-friend-search > div.friend-list > div"));
element.findElement(By.cssSelector("div.name-and-input > input")).sendKeys(applyContent);
// 点击发送
Thread.sleep(1000);
webDriver.findElement(By.cssSelector("div.apply > button")).click();
// 判断接受状态
assert webDriver.findElement(By.cssSelector("div.apply > button")).getText().equals("已发送") : "发送按钮点击后,状态显示异常";
traceless.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-request")).click();
Thread.sleep(1000);
// 截图
Common.FullScreenCapture("发送好友请求截图");
}
private void getAllScreenShot() throws InterruptedException {
// 1.选中session.截图
webDriver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-session")).click();
traceless.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-session")).click();
Common.FullScreenCapture("初始会话列表截图");
// 2.选中 好友列表,截图
Thread.sleep(250);
webDriver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-friend")).click();
traceless.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-friend")).click();
Common.FullScreenCapture("初始好友列表列表截图");
// 3.选中好友申请,截图
Thread.sleep(250);
webDriver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-request")).click();
traceless.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-request")).click();
Common.FullScreenCapture("初始好友申请列表截图");
}
}
6.发送消息功能测试代码
java
package chatroom.TestApi.Send;
import chatroom.Common.Common;
import chatroom.TestApi.Utils.Utils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.io.IOException;
import java.time.Duration;
import java.util.List;
public class SendMessage extends Utils {
private static final String URL = "http://127.0.0.1:9091/login.html";
public SendMessage() {
super(URL);
}
/**
* 发送消息
* @param flag (true 表示点击会话区域发送消息 false表示点击好友列表发送消息)
* @param row 发送几条消息
* @throws InterruptedException
* @throws IOException
*/
public void Message(boolean flag,int row) throws InterruptedException, IOException {
// 先打开两个浏览器
// 接着左右分屏
login("zhangsan","123456");
setLeftWindow(webDriver);
TraceLogin("testName","123456",URL,true);
// 双方进行发送消息验证
reallySendMessage(webDriver,traceless,true,row);
}
/**
* 发送消息(正式版,在 FriendApply 用到,此处将代码提取进行封装)
*/
public static void reallySendMessage(WebDriver webDriver,WebDriver traceless,boolean flag,int row) throws InterruptedException {
choiceSession(webDriver,true);// 这里先不用 flag,待后续逻辑补充
choiceSession(traceless,true);
// 一个一个给对方发消息
// 在这里,先获取当前的会话的数据,方便后续进行选中元素并校验
List<WebElement> elements = webDriver.findElements(By.cssSelector("body > div.client-container > div > div.right > div.message-show > div"));
// 消息 数组下标 dom元素下标
int message = 0,index = 0,eleCount = elements.size();
WebDriver[] list = new WebDriver[]{webDriver,traceless};
String send = null;
for(int i = 0;i < row;i++){
Thread.sleep(500);
send = send(list[index++], message++);
eleCount++;// 发送消息数目加一
// 对方查看消息框,检验消息是否正确
if(index >= 2)index %= list.length;// 更新数组下标
String text = list[index].findElement(By.xpath("/html/body/div[2]/div/div[2]/div[2]/div[" + eleCount + "]/div/p")).getText();
assert text.equals(send) : "发送的消息与接受的消息不一样";
// 接收方接受的消息没问题,那就开始发送
}
Common.FullScreenCapture("发消息校验"+Utils.getStaticCurrentMothed());
}
/**
* 选中对话
* @param driver
*/
private static void choiceSession(WebDriver driver,boolean flag) throws InterruptedException {
// 点击会话标签
WebElement webElement = slidePage("body > div.client-container > div > div.left > div.tab > div.tab-session");
webElement.click();
if(flag) driver.findElement(By.xpath("//*[@id=\"session-list\"]/li[1]")).click();
// else 扩展点击好友列表的好友,进入聊天的功能测试
}
private static String send(WebDriver driver,int message){
// 寻找输入框
// 输入元素
// 发送
String var = String.valueOf(message);
String ele = var + var + var;
driver.findElement(By.cssSelector("body > div.client-container > div > div.right > textarea")).sendKeys(ele);
driver.findElement(By.cssSelector("body > div.client-container > div > div.right > div.ctrl > button")).click();
return ele;
}
}
四.接口测试
1.登录接口

2.注册接口

3.首页接口

4.发送好友请求接口

5.发送消息接口

五.界面测试
(1).登录界面:
图片正确显示、不遮挡关键信息、页面无错别字:

信息提示正确,无遮挡



(2). 注册界面
图片正确显示、不遮挡关键信息、页面无错别字:
错误提示正确,无遮挡>:

(3).首页界面
首页元素无遮挡、显示排版正确,文字图标描述清晰>:

(4).添加好友界面
界面无遮挡、格式排版符合正常使用,文字正确,描述清晰>:

提示清晰准确,无遮挡>:

点击拒绝按钮>:

点击同意按钮,并创建会话>:

(5).发送消息
界面显示正确,信息排版正确,无遮挡、无错误>:

六.兼容性测试
①在Edge浏览器下进入网络聊天室
预期结果:无异常且一切布局显示功能等与其一致





②在Chrome浏览器下进入网络聊天室
预期结果:无异常且一切布局显示功能等与其一致

七.安全测试
场景一:未登录状况下,是否能访问除登陆注册外其他功能或板块
结果:无法访问除登陆注册外的其它板块

场景二:Fidder抓包
预期结果:对于敏感信息密文传输,无法获取具体的信息
结果:账号密码显示明文,协议应该升级为https

八.性能测试
1.聚合报告

a. 整体表现
- 总样本数:27230 次
- 平均响应时间:12 ms
- 异常率:1.93%(整体可控)
- 吞吐量:340.4/sec(系统处理能力较强)
b. 核心指标拆解
c. 重点问题接口
添加好友
- 平均响应 73ms,最大值 207ms,标准差 11.51(波动大)
- 异常率 14.10%,是故障重灾区
- 可能原因:数据库事务锁、好友关系校验逻辑复杂、网络 IO 阻塞
拒绝好友申请 / 登录其他用户
- 异常率 14.10%,和「添加好友」同步异常
- 推测是业务流程依赖:拒绝 / 添加操作依赖同一套用户状态校验,导致连锁失败
根据元素模糊查找用户
- 平均 50ms,最大值 173ms,标准差 7.85
- 可能原因:未建索引、模糊查询
LIKE效率低、数据量过大d.优化建议
- **优化索引:「用户表(friend)」「好友关系表(session_message_user)」**添加合理索引,优化模糊查询和关联查询
- 结果异步化处理: 重构「添加好友」等逻辑,减少锁竞争,将非核心操作异步化
- **缓存优化:**采用redis,优化查询速度
2.tps报告

a. 图表元素
- 横轴:时间(Elapsed time),粒度为 1 秒,覆盖测试全程(约 1 分 20 秒)。
- 纵轴:每秒事务数(Number of transactions/sec),反映接口每秒能处理的成功请求数。
- 多色折线:每条折线对应一个系统接口,图例标注了接口类型(如 "登录""添加好友""获取好友列表" 等),且均为 "success",代表成功事务的吞吐量。
b 阶段分析
流量爬坡阶段(0s ~ 15s)
- 表现:核心接口(如登录)TPS 从 0 快速飙升至~55 TPS 的峰值,其他接口同步爬坡。
- 原因:阶梯线程组逐步启动线程,系统瞬间接入大量流量,处于预热 + 负载适应状态。
- 波动:登录接口曲线剧烈抖动,说明高并发下系统资源(CPU、数据库、IO)开始出现竞争,处理能力尚未稳定。
稳定负载阶段(15s ~ 72s)
- 表现:登录接口 TPS 稳定在 40~55 区间,其他核心业务接口(如获取好友列表、发送消息)TPS 稳定在 12~40 区间,波动幅度逐渐收窄
- 描述:系统在持续压力下的真实处理能力,代表长期可支撑的业务吞吐量
- 详细 :
- 登录接口流量占比最高,是当前压测的核心流量入口
- 其他业务接口流量分布均匀,核心流程整体稳定
- 失败类事务(如添加好友失败、拒绝好友申请失败)TPS 极低,说明业务成功率较高
流量衰减阶段(72s 之后)
- 表现:所有接口 TPS 逐步下降,最终趋近于 0。
- 原因:阶梯线程组开始按配置逐步停止线程,负载自然下降,曲线平滑回落
c. 吞吐量趋势
(1).总结

(2). 原因分析
- 读操作(如获取好友列表、查询历史记录):TPS 相对较高,能支撑 30~40 的稳定吞吐量
- 写操作(如添加好友、发送消息):TPS 较低,稳定在 12~20 区间,存在锁竞争或数据库写入瓶颈
- 登录接口:负载最高,是当前系统的主要压力来源
(3). 优化方向
1> 加索引:对用户表、好友关系表添加合理索引,优化模糊查询和关联查询,避免全表扫描
2> Redis 缓存:将用户信息、好友列表、会话消息等高频读请求缓存到 Redis,减轻数据库读压力,提升查询速度
3> 异步化:重构 "添加好友" 等写操作逻辑,减少锁竞争,将非核心操作(如通知、统计)放入消息队列异步处理
3.响应时间可视化

a. 图表结构与元素
- 横轴:时间(从 00:00:00 到 00:01:20 左右),反映测试的时间进程。
- 纵轴:响应时间(单位 ms),体现接口处理请求的耗时。
- 多色折线:每条折线对应一个系统接口,图例标注了接口类型,如 "登录""添加好友""获取好友列表""发送消息" 等。
b. 性能趋势分析
1. 整体性能表现
- 响应时间分布 :大部分接口响应时间集中在 10--80ms,整体表现稳定。
- 波动趋势 :随着压测时间推进,首页 、发送消息等核心接口响应时间逐步上升,存在一定的性能衰减。
- 峰值情况 :最高响应时间出现在 添加好友 接口,峰值约 175ms。
2. 接口性能分析
c. 关键问题与风险点
1. 核心接口性能衰减
- 添加好友 、发送消息 、首页 等接口,响应时间从初始的 30--50ms 逐步攀升至 70--95ms,说明系统在持续压测下存在资源耗尽或处理延迟。
2. 瞬时尖峰问题
- 多个接口出现瞬时尖峰(如添加好友 峰值 175ms),可能由以下原因导致:
- 数据库慢查询(如多表关联、未加索引)
- 垃圾回收(GC)停顿
- 线程阻塞或锁竞争(如添加好友时的事务锁)
- 数据库连接池耗尽
- 热点接口集中
- 高耗时接口多集中在用户交互类写操作(添加好友、发送消息),这类操作涉及数据写入与事务,是性能优化的核心目标。
d. 优化方向
- 加索引:对用户表、好友关系表添加合理索引,优化模糊查询和关联查询,避免全表扫描。
- Redis 缓存:将用户信息、好友列表、首页数据等高频读请求缓存到 Redis,减轻数据库读压力。
- 异步化:重构 "添加好友""发送消息" 等写操作逻辑,减少锁竞争,将非核心操作(如通知、统计)放入消息队列异步处理。
- 连接池优化:调整数据库连接池大小,避免连接耗尽导致的响应延迟。
4.测试报告





错误描述>:
**(1).**本次性能测试中:
总错误数: 48
总样本数: 19855
说明:大部分请求是成功的,只有约 0.24% 的请求因断言不通过标记为失败,且主要集中在返回 6003 的场景(删除失败,该聊记录不存在或已被删除)
**(2).**可能原因>:
a.网络波动
• 测试环境或生产环境的网络链路不稳定(如丢包、延迟突然升高、带宽拥堵),导致请求 / 响应数据传输耗时增加。
• 客户端与服务器之间的网络路由跳转过多,也可能引发响应时间波动。
b.并发请求处理能力不足
同一时间发起大量请求(如批量获取数据、高并发场景),服务器资源(CPU、内存、线程池)被占满,无法及时处理新请求,导致排队等待时间过长,以及数据被处理了,但没有同步更新
c.数据状态变化:
比如删除了好友请求,本应该消失的数据却依然显示,导致后续点击出现6003错误
九.总结
一、测试范围与内容
1.功能测试
• 注册、登录、好友管理、添加 / 拒绝 / 同意好友、发送消息、会话列表、首页展示
• 空值、错误参数、未授权访问等异常场景校验
2.自动化测试
基于 Selenium 实现 WebUI 自动化,覆盖登录、注册、首页、好友、发消息等核心流程
包含公共方法、断言、截图、多浏览器并行验证
3.接口测试(JMeter)
登录 / 注册 / 首页 / 模糊搜索 / 好友申请 / 发消息 / WebSocket 等全接口
断言、参数化、循环、结果树校验
4.界面测试
布局、文字、提示、图标、交互无遮挡、无错乱、提示准确
5.兼容性测试
Chrome、Edge 浏览器:功能与显示完全一致
6.安全测试
未登录拦截正常;账号密码明文传输,未用 HTTPS
7.性能测试(JMeter)
聚合报告、TPS、响应时间、APDEX、错误统计
二、核心测试结果
1. 功能与界面
• 所有业务流程正常可用
• 输入校验、弹窗提示、页面跳转符合预期
• 界面排版正常、无遮挡、无错别字
2. 兼容性
Chrome、Edge完全兼容,无差异
3. 安全性(主要问题)
✅ 未登录无法访问业务页面,拦截有效
❌ 登录密码明文传输,未使用 HTTPS,存在泄露风险
4. 性能(关键结论)
(1).总样本:19855,失败 48,错误率 0.24%
(2).APDEX 0.995,整体体验优秀
(3).吞吐量:248.55/sec,处理能力较强
(4).平均响应:3.73ms,多数接口极快
(5).性能瓶颈接口
• 添加好友:平均 19.39ms,最大 52ms
• 拒绝好友 / 登录其他用户:错误率 3.61%
• 模糊搜索用户:响应偏慢,无索引导致
• 错误原因:返回 6003(数据状态不同步、记录不存在)
三、主要问题汇总
• 安全:密码明文、未使用 HTTPS
• 性能:添加好友、模糊查询慢;好友相关操作偶发失败
• 稳定性:高并发下数据状态不同步,导致 6003 错误
• 数据库:缺少索引,写操作锁竞争明显
四、优化建议
• 安全:升级 HTTPS,密码加密传输
• 数据库:用户表、好友表加索引,优化模糊查询
• 性能:Redis 缓存好友 / 会话列表;添加好友异步化
• 稳定性:修复数据状态同步问题,优化并发事务
五、整体总结
本次针对网络聊天室项目开展了功能、界面、兼容性、安全、接口自动化及性能等多方面全面测试。结果表明:项目核心业务流程完整可用,界面展示正常,多浏览器兼容性良好,接口响应速度快,整体用户体验优秀。
但项目仍存在明显不足:密码明文传输存在安全隐患;高并发场景下部分接口出现请求失败,错误率偏高;模糊查询、好友相关操作响应较慢,受数据库索引缺失、锁竞争及并发数据不同步影响。
整体而言,系统基础功能已满足使用要求 ,性能与稳定性基本达标,但在安全防护、高并发稳定性、数据库效率上存在短板,经针对性优化完善后,可满足更稳定、安全的上线运行条件。














