1. 项目背景

这是一个基于 Spring Boot + WebSocket 的前后端分离聊天项目。
本项目是一款集用户登录、实时通讯、消息记录管理于一体的即时通讯类应用,由本人独立完成 UI 设计、前后端开发、接口调试及云服务器部署的全流程工作。

2. 测试目标及测试任务概述

验证网页聊天程序各个功能的完整性,以及确保每个功能下的核心模块是正确的。

3. 项目模块及实现功能

  • 注册功能:实现用户账号注册,包含用户名、密码等信息校验与存储,支持账号唯一性校验,确保注册流程安全可靠。
  • 登录功能:基于用户名/密码完成身份验证,通过拦截器校验登录状态,生成会话标识,实现登录态持久化与权限控制。
  • 获取登录用户:基于用户名/密码完成身份验证,通过拦截器校验登录状态,生成会话标识,实现登录态持久化与权限控制。
  • 获取好友列表:查询并展示当前用户的好友列表,支持按在线/离线状态筛选,为即时通讯提供联系人入口。
  • 获取会话信息:拉取与指定好友的历史聊天记录,支持分页加载与最后一条消息记录,保障消息查看的连贯性与完整性。
  • 消息的实施传输:基于 WebSocket 实现消息的双向实时推送,支持文本消息发送与接收,确保低延迟、高可靠的即时通讯体验。

4. 测试用例

在这里插入图片描述

5. 自动化测试

公共类Utils
创建下载驱动 createDriver()
屏幕截图 screenShot()
退出浏览器 quit()

package common;

import io.github.bonigarcia.wdm.WebDriverManager;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.Duration;

public class Utils {

    public static WebDriver driver = null;
    public static WebDriverWait wait = null;

    public Utils(String url){
        driver = createDriver();
        driver.get(url);
        //显示的等待
        wait = new WebDriverWait(driver, Duration.ofSeconds(3));
    }


    //1.创建driver对象,下载驱动
    public WebDriver createDriver(){
        if(driver == null){
            WebDriverManager.chromedriver().setup();
            ChromeOptions options = new ChromeOptions();
            options.addArguments("--remote-allow-origins=*");
            driver = new ChromeDriver(options);
           //添加隐式等待
            driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
        }
        return driver;
    }
    //屏幕截图
    public  void screenShot(String str) throws IOException {
        //表示要转换的格式
        SimpleDateFormat sim1 = new SimpleDateFormat("yyy-MM-dd");
        SimpleDateFormat sim2 = new SimpleDateFormat("HH_mm_ss");

        //将当前系统的时间转换为上述的格式
        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));
    }
    //退出
    public void quit(){
        driver.quit();
    }

}

5.1 注册功能自动化测试

检查页面元素是否存在 checkPageRight()
成功注册 registerRight()
错误的注册 --错误的用户名,正确的密码:registerFail()

import org.openqa.selenium.support.ui.ExpectedConditions;

import java.io.IOException;

public class RegisterTest extends Utils {
    public static String url = "http://60.205.7.164:8088/register.html";
   
    public RegisterTest(){
        super(url);
    }

    //检查页面元素是否存在
    public void checkPageRight() throws IOException {

        //屏幕截图
        screenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
        String name = driver.findElement(By.cssSelector("body > div.nav")).getText();

        //用户名输入框,密码输入框
        driver.findElement(By.cssSelector("#username"));
        driver.findElement(By.cssSelector("#password"));
        //注册按钮
        driver.findElement(By.cssSelector("#submit"));

        assert name.equals("网页聊天");
    }

    //正确的注册
    public void registerRight() throws InterruptedException, IOException {
        //清空输入框的内容
        driver.findElement(By.cssSelector("#username")).clear();
        driver.findElement(By.cssSelector("#password")).clear();

        driver.findElement(By.cssSelector("#username")).sendKeys("xiaohong");
        driver.findElement(By.cssSelector("#password")).sendKeys("123");
         //点击注册按钮
        driver.findElement(By.cssSelector("#submit")).click();

        //等待弹窗出现
        wait.until(ExpectedConditions.alertIsPresent());
        //出现弹窗
        Alert alert =driver.switchTo().alert();
        //确认
        alert.accept();

        //等待两秒,截图查看是否跳转到登录界面
        Thread.sleep(2000);
        screenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
    }

    //错误的注册
    public void  {
        //清空输入框的内容
        driver.findElement(By.cssSelector("#username")).clear();
        driver.findElement(By.cssSelector("#password")).clear();

        //错误的用户名,和空的密码
        driver.findElement(By.cssSelector("#username")).sendKeys("xiaowang");
        driver.findElement(By.cssSelector("#submit")).click();
        //等待弹窗出现
        Alert content =  wait.until(ExpectedConditions.alertIsPresent());
        //获取弹窗的内容
       String text = content.getText();
       assert  text.equals("输入的用户名或密码为空");
    }

}

5.2 登录功能自动化测试

页面元素是否存在 checkPageRight()
正常登录 loginRight()
错误的登录–错误的用户名和空的密码 loginFail()
正确的用户名,错误的密码 loginFailTwo()

package cases;

import common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.support.ui.ExpectedConditions;

import java.io.IOException;

public class LoginTest extends Utils {
    public static String url = "http://60.205.7.164:8088/login.html";
    public LoginTest(){
        super(url);
    }
    //检查页面元素是否存在
    public void checkPageRight() throws IOException {

        //屏幕截图
        screenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
        String name = driver.findElement(By.cssSelector("body > div.nav")).getText();

        //用户名输入框,密码输入框
        driver.findElement(By.cssSelector("#username"));
        driver.findElement(By.cssSelector("#password"));
        //登录按钮
        driver.findElement(By.cssSelector("#submit"));

        assert name.equals("网页聊天");
    }

    //正确的登录
    public   void loginRight() throws InterruptedException, IOException {
        //清空输入框的内容
        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("123");
        //点击登录按钮
        driver.findElement(By.cssSelector("#submit")).click();

        //等待弹窗出现
        wait.until(ExpectedConditions.alertIsPresent());
        //出现弹窗
        Alert alert =driver.switchTo().alert();
        //确认
        alert.accept();

        //等待两秒,截图查看是否跳转到客户端界面
        Thread.sleep(2000);
        screenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
    }

    //错误的登录
    public void loginFail(){
        //清空输入框的内容
        driver.findElement(By.cssSelector("#username")).clear();
        driver.findElement(By.cssSelector("#password")).clear();

        //错误的用户名,和空的密码
        driver.findElement(By.cssSelector("#username")).sendKeys("zhangsan");
        driver.findElement(By.cssSelector("#submit")).click();
        //等待弹窗出现
        Alert content =  wait.until(ExpectedConditions.alertIsPresent());
        //获取弹窗的内容
        String text = content.getText();
        assert  text.equals("当前输入的用户名或密码为空");
    }
    //错误的登录
    public void loginFailTwo(){
        //清空输入框的内容
        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("1233");

        driver.findElement(By.cssSelector("#submit")).click();
        //等待弹窗出现
        Alert content =  wait.until(ExpectedConditions.alertIsPresent());
        //获取弹窗的内容
        String text = content.getText();
        assert  text.equals("登录失败!");
    }


}

5.3客户端界面的测试

检查页面元素checkClient()
检查会话列表下的页面元素 是否存在checkSession()
检查好友列表下的元素是否存在checkFriend()

package cases;

import common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;

import java.io.IOException;
import java.util.List;



public class ClientTest extends Utils {
   public static String url ="http://60.205.7.164:8088/client.html";
    public ClientTest(){
        super(url);
    }
    //检查页面元素
    public void checkClient() throws IOException, InterruptedException {
        wait.until(ExpectedConditions.alertIsPresent());
        Alert alert = driver.switchTo().alert();
        alert.accept();
        LoginTest loginTest = new LoginTest();
        loginTest.loginRight();
        //1.检查左侧列表上方是否显示登录用户名
        String name = driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.user")).getText();
        assert name.equals("zhangsan");

        //2.检查搜索框是否存在
        driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.search > input[type=text]"));
        //3.检查会话图标,好有图标是否存在
        driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-session"));
        driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-friend"));

        //4.检查发送按钮是否存在
        driver.findElement(By.cssSelector("body > div.client-container > div > div.right > div.ctrl > button"));
    }


    //检查会话列表下的元素都是否存在
    public  void checkSession() throws IOException, InterruptedException {
        wait.until(ExpectedConditions.alertIsPresent());
        Alert alert = driver.switchTo().alert();
        alert.accept();
        LoginTest loginTest = new LoginTest();
        loginTest.loginRight();

        String name = driver.findElement(By.cssSelector("#session-list > li:nth-child(1) > h3")).getText().trim();
        List<WebElement> ele = driver.findElements(By.cssSelector("#session-list > li:nth-child(1) > p"));
        if (name.isEmpty()) {
            System.out.println("没有和好友进行聊天");
        } else {
            System.out.println("和好友进行了聊天");
        }

        if (ele.isEmpty()) {
            System.out.println("和好友还没有进行聊天!");
        } else {
            System.out.println("和好友进行了聊天!");
        }

        Thread.sleep(2000);
        screenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
    }

    //检查当前好友列表的元素
    public void checkFriend() throws IOException, InterruptedException {
        wait.until(ExpectedConditions.alertIsPresent());
        Alert alert = driver.switchTo().alert();
        alert.accept();
        LoginTest loginTest = new LoginTest();
        loginTest.loginRight();

        driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-friend")).click();
        String friend = driver.findElement(By.cssSelector("#friend-list > li:nth-child(1) > h4")).getText().trim();

        if(friend.isEmpty()){
            System.out.println("当前用户没有好友!");
        }else{
            System.out.println("当前用户的好友:"+friend);
        }

        Thread.sleep(2000);
        screenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
    }


}

5. 4会话功能测试

当点击会话列表的图标sessionList()
当点击好友列表的图标 friendList()
当点击某个好友列表下的好友时 friendChat()
点击会话列表下的最后一个好友,进行消息发送,查看发送消息之后,是否会顶置 pinnedTest()

package cases;

import common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;

import java.io.IOException;
import java.util.List;

public class ListTest extends Utils {
   public static String url ="http://60.205.7.164:8088/client.html";
    public ListTest(){
        super(url);

    }
    //当点击会话列表的图标
    public void sessionList() throws IOException, InterruptedException {
        wait.until(ExpectedConditions.alertIsPresent());
        Alert alert = driver.switchTo().alert();
        alert.accept();
        LoginTest loginTest = new LoginTest();
        loginTest.loginRight();

        //点击会话列表图标,查看是否好友列表隐藏
        driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-session")).click();
        //会话列表的class属于是显示状态
        String session = driver.findElement(By.cssSelector("#session-list")).getAttribute("class");
        String friend = driver.findElement(By.cssSelector("#friend-list")).getAttribute("class");

        assert session.equals("list");
        assert friend.equals("list hide");

        Thread.sleep(2000);
        screenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
    }

    //当点击好友列表的图标
    public void friendList() throws IOException, InterruptedException {
        wait.until(ExpectedConditions.alertIsPresent());
        Alert alert = driver.switchTo().alert();
        alert.accept();
        LoginTest loginTest = new LoginTest();
        loginTest.loginRight();

        //点击好友列表图标,查看是否会话列表隐藏
        driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-friend")).click();
        //好友列表的class属于是显示状态
        String session = driver.findElement(By.cssSelector("#session-list")).getAttribute("class");
        String friend = driver.findElement(By.cssSelector("#friend-list")).getAttribute("class");

        assert session.equals("list hide");
        assert friend.equals("list");
        Thread.sleep(2000);
        screenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
    }

    //当点击某个好友列表下的好友时
    public void friendChat() throws IOException, InterruptedException {
        wait.until(ExpectedConditions.alertIsPresent());
        Alert alert = driver.switchTo().alert();
        alert.accept();
        LoginTest loginTest = new LoginTest();
        loginTest.loginRight();

        driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-friend")).click();
        List<WebElement> ele = driver.findElements(By.cssSelector("#friend-list"));
        if(ele.isEmpty()){
            System.out.println("当前登录用户没有好友");
        }else{
           String friendName = driver.findElement(By.cssSelector("#friend-list > li:nth-child(1) > h4")).getText();
            driver.findElement(By.cssSelector("#friend-list > li:nth-child(1) > h4")).click();

            //1.查看是否跳转到会话好友列表
            String session = driver.findElement(By.cssSelector("#session-list")).getAttribute("class");
            String friend = driver.findElement(By.cssSelector("#friend-list")).getAttribute("class");

            assert session.equals("list");
            assert friend.equals("list hide");

            //2.检查会话列表第一个是不是当前点击的好友
            String name = driver.findElement(By.cssSelector("#session-list > li.selected > h3")).getText();
            assert name.equals(friendName);
            //检查是否是被选中状态
            String lable = driver.findElement(By.cssSelector("#session-list > li.selected")).getAttribute("class");
            assert lable.equals("selected");

            //3.检查最后一条消息是否是正确的
            String messageLast =driver.findElement(By.cssSelector("#session-list > li.selected > p")).getText();
            List<WebElement> message = driver.findElements(By.cssSelector(".message-show > div.message"));
            if(message.isEmpty() && messageLast.isEmpty()){
                System.out.println("聊天记录为空!");
            }else {
                int size = message.size();

                String cssStr = "body > div.client-container > div > div.right > div.message-show > div:nth-child(" + size + ") > div > p";
                String last = driver.findElement(By.cssSelector(cssStr)).getText().trim();
                assert messageLast.equals(last);

            }
            //4.会话列表区的好友和消息区展示的好友是否是一样的
            String chatFriend = driver.findElement(By.cssSelector("body > div.client-container > div > div.right > div.title")).getText();
            assert name.equals(chatFriend);
        }
    }

    //点击会话列表下的最后一个好友,进行消息发送,查看发送消息之后,是否会顶置
    public void pinnedTest() throws IOException, InterruptedException {
        wait.until(ExpectedConditions.alertIsPresent());
        Alert alert = driver.switchTo().alert();
        alert.accept();
        LoginTest loginTest = new LoginTest();
        loginTest.loginRight();

        List<WebElement> unSelectedList = driver.findElements(By.cssSelector("#session-list > li:not(.selected)"));
        if(unSelectedList.isEmpty()){
            System.out.println("没有选中会话");
        }else {
            unSelectedList.get(0).click();
           String  state = unSelectedList.get(0).getAttribute("class");
           assert state.equals("selected");
            String name = unSelectedList.get(0).findElement(By.cssSelector("h3")).getText();
            //在输入框输入内容
            driver.findElement(By.cssSelector("body > div.client-container > div > div.right > textarea")).sendKeys("你好");
            driver.findElement(By.cssSelector("body > div.client-container > div > div.right > div.ctrl > button")).click();

            //查看指定的元素是否是当前的name
            String chatUsername =driver.findElement(By.cssSelector("#session-list > li.selected > h3")).getText();
             assert  name.equals(chatUsername);

        }

    }

}

5.5 消息传输功能测试

成功发送消息sendSuccess()
消息发送失败—不输入内容直接发送 sendFail()
查看发送消息的左右两侧是否是正确的名称 messageTest()
消息的发送messageTrans()

package cases;

import common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;

import java.io.IOException;
import java.util.List;

public class MessageTest extends Utils {
   public static String url ="http://60.205.7.164:8088/client.html";
    public MessageTest(){
        super(url);
    }

    //成功发送消息
    public void sendSuccess() throws IOException, InterruptedException {
        wait.until(ExpectedConditions.alertIsPresent());
        Alert alert = driver.switchTo().alert();
        alert.accept();
        LoginTest loginTest = new LoginTest();
        loginTest.loginRight();

        List<WebElement> unSelectedList = driver.findElements(By.cssSelector("#session-list > li:not(.selected)"));
        if(unSelectedList.isEmpty()){
            System.out.println("没有选中会话");
        }else {
            unSelectedList.get(0).click();

            Thread.sleep(2000);
            driver.findElement(By.cssSelector("body > div.client-container > div > div.right > textarea")).sendKeys("hello");
            //点击发送
            driver.findElement(By.cssSelector("body > div.client-container > div > div.right > div.ctrl > button")).click();
            //选中标签是message-right的查看名称是否是好友
            List<WebElement> messageRight = driver.findElements(By.cssSelector(".message-show > div.message.message-right"));
            if(messageRight.isEmpty()){
                System.out.println("当前登录用户发送消息失败");
            }else{
                int size = messageRight.size();
                Thread.sleep(2000);
                String content=  messageRight.get(size-1).findElement(By.cssSelector(".box >p")).getText().trim();
                assert content.equals("hello");
            }

        }
        Thread.sleep(2000);
        screenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
    }

    //消息发送失败---不输入内容直接发送
    public void sendFail() throws IOException, InterruptedException {
        wait.until(ExpectedConditions.alertIsPresent());
        Alert alert = driver.switchTo().alert();
        alert.accept();
        LoginTest loginTest = new LoginTest();
        loginTest.loginRight();

        List<WebElement> unSelectedList = driver.findElements(By.cssSelector("#session-list > li:not(.selected)"));
        if(unSelectedList.isEmpty()){
            System.out.println("没有选中会话");
        }else {
            unSelectedList.get(0).click();

            Thread.sleep(2000);
            //点击发送
            driver.findElement(By.cssSelector("body > div.client-container > div > div.right > div.ctrl > button")).click();
        }
        Thread.sleep(2000);
        screenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
    }

    //查看发送消息的左右两侧是否是正确的名称
    public void messageTest() throws IOException, InterruptedException {
        wait.until(ExpectedConditions.alertIsPresent());
        Alert alert = driver.switchTo().alert();
        alert.accept();
        LoginTest loginTest = new LoginTest();
        loginTest.loginRight();

        List<WebElement> unSelectedList = driver.findElements(By.cssSelector("#session-list > li:not(.selected)"));
        if(unSelectedList.isEmpty()){
            System.out.println("没有选中会话");
        }else {
            unSelectedList.get(0).click();
            //获取到当前徐那种的好友名字
            String chatFriend =  unSelectedList.get(0).findElement(By.cssSelector("h3")).getText();
            //选中标签是message-left的查看名称是否是好友
            List<WebElement> messageLeft = driver.findElements(By.cssSelector(".message-show > div.message.message-left"));
            if(messageLeft.isEmpty()){
                System.out.println("好友还没有和你进行对话");
            }else{
               String friendName =  messageLeft.get(0).findElement(By.cssSelector(".box >h4")).getText();
               assert friendName.equals(chatFriend);
            }
            //选中标签是message-right的查看名称是否是好友
            List<WebElement> messageRight = driver.findElements(By.cssSelector(".message-show > div.message.message-right"));
            if(messageRight.isEmpty()){
                System.out.println("当前登录用户还没有进行对话");
            }else{
                String friendName =  messageRight.get(0).findElement(By.cssSelector(".box >h4")).getText().trim();
                assert friendName.equals("zhangsan");
            }
        }
        Thread.sleep(2000);
        screenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
    }

    //消息的发送
    public void messageTrans() throws IOException, InterruptedException {
        sendSuccess();
        List<WebElement> unSelectedList = driver.findElements(By.cssSelector("#session-list > li:not(.selected)"));
        if (unSelectedList.isEmpty()) {
            System.out.println("没有选中会话");
        }
        String name = unSelectedList.get(0).findElement(By.cssSelector("h3")).getText().trim();
        TalkTest talkTest = new TalkTest();
        talkTest.login(name);
        //获取好友的会话列表
        Thread.sleep(2000);
        String friend =  driver.findElement(By.cssSelector("#session-list > li > h3")).getText();
        assert friend.equals("zhangsan");
        String content = driver.findElement(By.cssSelector("#session-list > li > p")).getText();
        assert content.equals("hello");
        //点击消息区,看最后一条是否是刚刚发送的内容
        driver.findElement(By.cssSelector("#session-list > li")).click();
        List<WebElement> message = driver.findElements(By.cssSelector(".message-show > div.message"));
        int size = message.size();
        String cssStr = "body > div.client-container > div > div.right > div.message-show > div:nth-child(" + size + ") > div > p";
        String last = driver.findElement(By.cssSelector(cssStr)).getText().trim();
        assert last.equals("hello");

       //获取最后一条消息的class标签是否是在左边
        //body > div.client-container > div > div.right > div.message-show > div:nth-child(18)
        String cssStr1  = "body > div.client-container > div > div.right > div.message-show > div:nth-child("+size+")";
        String lable = driver.findElement(By.cssSelector(cssStr1)).getAttribute("class");
        assert lable.contains("message message-left") : "最后一条消息不是左侧,class=" + lable;
    }


}

6. 接口测试

在这里插入图片描述

6.1获取好友列表

在这里插入图片描述

6.2 获取所有会话信息

在这里插入图片描述

6.3获取历史消息

在这里插入图片描述

7. 性能测试

7.1 接口性能测试

7.1.1聚合报告

在这里插入图片描述

7.1.2 TPS可视化

在这里插入图片描述
1.图标结构与描述

  • 横轴 :时间(从 00:00:00 到 00:01:46 左右),反映测试的时间进程。
  • 纵轴:每秒事务数(Transactions/sec),体现系统在单位时间内成功处理的请求量。
  • 多色折线 :每条折线对应一个系统接口,图例标注了接口类型,如 “登录请求”“获取具体的会话历史消息”“获取好友列表” 等,且均标注为 (success),表示统计的是成功请求的吞吐量。

2.图标结构与描述

  • 启动阶段(00:00:00 - 00:00:10):各接口的每秒事务数快速攀升,在约 10 秒时达到峰值(接近 90 次 / 秒),说明系统在压测启动后迅速进入高负载状态,成功处理了大量并发请求。

  • 稳定阶段(00:00:10 - 00:01:35):吞吐量在 60-70 次 / 秒的区间内小幅波动,整体保持平稳,表明系统在持续负载下能够稳定处理请求,吞吐量未出现明显衰减。

  • 结束阶段(00:01:35 之后):所有接口的每秒事务数急剧下降至接近 0,这是压测结束、并发线程停止的正常表现,而非系统性能问题。

7. 1.3响应时间可视化

在这里插入图片描述
1.图标结构与描述

  • 横轴 :时间(从 00:00:00 到 00:01:46 左右),反映测试的时间进程。
  • 纵轴:响应时间(单位 ms),体现接口处理请求的耗时。
  • 多色折线 :每条折线对应一个系统接口,图例标注了接口类型,如 “登录请求”“获取具体的会话历史消息”“获取好友列表” 等。

2.性能趋势分析

  • 平稳阶段(00:00:00 - 00:01:35):除 “获取具体的会话历史消息” 外,其他接口(登录请求、获取好友列表等)的响应时间始终保持在极低水平,曲线几乎贴近横轴,说明在压测的大部分时间里,这些接口性能稳定,响应迅速。

  • 拐点阶段(00:01:35 之后):“获取具体的会话历史消息” 接口的响应时间出现了急剧飙升,从接近 0ms 直接飙升至近 60000ms(即 60 秒),这是一个非常明显的性能拐点。

7.1.4性能测试报告

在这里插入图片描述
1.接口差异

  • 获取好友列表:APDX最高,性能最优。
  • “获取会话历史消息”:APDX最低的,是相对处于短板。

2.请求与错误统计

  • 请求成功率:所有接口请求成功率为100%,无任务失败请求,系统稳定性较佳。

  • 总样本量:共执行 31792 次请求,覆盖充分,压测结果可靠。

  • 短板:“获取具体的会话历史消息” 的最大响应时间达到 29143ms(约 29 秒),存在极端长尾请求,是主要性能瓶颈。
  • 问题原因推测:
  • 1.该接口可能涉及大量历史消息的数据库查询,且未建立有效索引,导致在高并发下查询效率极低。
    2.也可能是消息存储未做分页或缓存,一次性加载过多数据,导致内存和 IO 资源耗尽。
  • 优化 :对消息表建立合理的索引; 实现消息分页加载,避免一次性返回太多数据;引入redis缓存,缓存用户最近的历史消息,减少数据库的压力。

7.2Websocket消息发送接收性能测试

7.2.1 聚合报告

在这里插入图片描述

7.2.2 TPS可视化

在这里插入图片描述
1.图标结构与描述

  • 横轴 :时间(从 00:00:00 到 00:01:46 左右),反映测试的时间进程。
  • 纵轴:每秒事务数(Transactions/sec),体现系统在单位时间内成功处理的请求量。
  • 多色折线 :每条折线对应一个系统接口,图例标注了操作类型,如 “登录请求”“WebSocket 连接”“WebSocket 发送消息”“WebSocket 接收消息”“WebSocket Close”,且均标注为 (success),表示统计的是成功请求的吞吐量。

2.性能趋势分析

  • 启动阶段(00:00:00 - 00:00:10):各操作的每秒事务数快速攀升,在约 10 秒时达到峰值(接近 20 次 / 秒),说明系统在压测启动后迅速进入高负载状态,成功处理了大量并发请求。
  • 稳定阶段(00:00:10 - 00:01:20):吞吐量在 2-12 次 / 秒的区间内小幅波动,整体保持平稳,表明系统在持续负载下能够稳定处理请求,吞吐量未出现明显衰减。
  • 结束阶段(00:01:20 之后):所有操作的每秒事务数急剧下降至接近 0,这是压测结束、并发线程停止的正常表现,而非系统性能问题。

吞吐量表现稳定:各操作的吞吐量曲线高度同步,说明压测场景下的请求分布均匀,系统对不同操作的处理能力均衡。

7.2.3 响应时间可视化

在这里插入图片描述

1.图标结构与描述

  • 横轴 :时间(从 00:00:00 到 00:01:28 左右),反映测试的时间进程。
  • 纵轴:响应时间(单位 ms),体现接口处理请求的耗时。

2.性能趋势分析

  • WebSocket 接收消息(青色):响应时间始终稳定在约 3000ms(3 秒),曲线几乎是一条直线,说明该操作的延迟是持续且固定的,是整个系统最严重的性能瓶颈。
  • WebSocket 发送消息(绿色):
    响应时间始终接近 0ms,表现极为优异,满足即时通讯场景下的低延迟需求。
  • WebSocket Close(深棕色):
    响应时间始终稳定在 0-100ms 之间,表现平稳,无明显波动。
  • 登录请求(红色)与 WebSocket 连接(紫色):
    前期(00:00:00 - 00:00:45):响应时间稳定在 0-300ms,表现良好。
    后期(00:00:45 之后):响应时间出现剧烈波动,峰值可达 1000ms 以上,甚至接近 1200ms,说明在持续负载下,登录和连接操作的性能出现了明显衰减。

3.结论

  • 发送性能突出:WebSocket 发送消息几乎无耗时,性能表现完美。
  • 接收性能瓶颈:WebSocket 接收消息的响应时间固定在 3 秒,严重影响用户体验,是最核心的性能短板。
  • 后期性能衰减:登录请求和 WebSocket 连接在压测后期出现明显的响应时间波动和峰值,说明系统在持续高负载下的稳定性不足。
  • 关闭操作稳定:WebSocket Close 操作始终表现平稳,无性能问题。

4.优化建议
1.WebSocket 接收消息优化:

  • 检查服务端消息推送逻辑,避免在接收消息时执行同步的数据库查询或其他耗时操作。
  • 引入消息队列(进行异步处理,降低同步阻塞 )优化客户端消息处理逻辑,避免在主线程中处理大量消息。

2.登录与连接优化:

  • 调整服务端线程池和连接池配置,避免资源耗尽导致超时。
  • 对登录态和连接信息进行缓存,减少重复认证和连接建立的开销。

7.2.4 性能测试报告

在这里插入图片描述
1.请求与错误统计
1.WebSocket 接收消息优化:

  • 请求成功率:整体请求成功率为 99.92%,仅出现 1 次失败,系统稳定性极佳。

  • 错误详情:唯一的错误是 “登录请求” 接口的连接超时(Connection timed out),属于偶发的网络或服务端负载问题,而非系统性缺陷。

2.存在短板:

  • WebSocket 接收消息是最严重的性能瓶颈,平均响应时间高达 3007.11ms(约 3 秒),且所有百分位均在 3000ms 以上,说明延迟是持续存在的。

3.优化:

  • 服务端优化:检查 WebSocket 服务端的消息推送逻辑,避免在接收消息时进行耗时操作(如数据库查询),引入消息队列异步处理。
  • 连接池优化:调整 WebSocket 连接池和登录服务的线程池配置,避免连接数过多导致的资源竞争和超时。
  • 缓存策略:对登录态和连接信息进行缓存,减少重复认证和连接建立的开销。

8. 兼容性测试

场景一:Chrome浏览器测试

在这里插入图片描述

在这里插入图片描述

场景二:edge浏览器测试
在这里插入图片描述
在这里插入图片描述

9. 安全测试

未登录状态下,是否可以进行客户端界面的访问?
结果 不可以进行访问的

在这里插入图片描述

10. 网络测试

有网络下可以正常访问:
在这里插入图片描述

无网络不能正常访问:
在这里插入图片描述

11 . 测试总结

1. 自动化测试:
针对注册、登录、客户端界面、会话功能、消息传输等核心模块,设计了完整的自动化测试用例,覆盖页面元素校验、功能流程验证和异常场景测试。

  • 注册功能:通过 checkPageRight() 检查页面元素,registerRight() 验证正常注册流程,registerFail() 覆盖错误用户名 + 正确密码等异常场景。
  • 登录功能:通过 checkPageRight() 检查页面元素,loginRight() 验证正常登录,loginFail()/loginFailTwo() 覆盖错误用户名、空密码、正确用户名 + 错误密码等异常场景。
  • 客户端界面:通过 checkClient() 检查客户端整体元素,checkSession() 验证会话列表元素,checkFriend() 验证好友列表元素,保障界面展示正确性。
  • 会话功能:通过 sessionList() 验证会话列表点击,friendList() 验证好友列表点击,friendChat() 验证好友聊天入口,pinnedTest() 验证消息发送后会话置顶功能。
  • 消息传输功能:通过 sendSuccess() 验证正常消息发送,sendFail() 覆盖空内容发送失败场景,messageTest() 校验消息发送者 / 接收者名称展示,messageTrans() 验证消息传输流程。

2.接口测试:
针对登录、获取登录用户信息,好友列表,获取会话信息,历史消息等接口,通过率95%,发现并修复接口逻辑漏洞 3处。

3.性能测试:
针对接口性能测试:
所有接口的 APDEX 评分都在 0.960 以上,整体为 0.987,说明在大部分时间里用户体验优秀。
阈值(T)为 500ms,而 “获取具体的会话历史消息” 的最大响应时间远超阈值,导致其 APDEX 相对最低(0.960),是影响用户体验的主要环节。

4.兼容性测试:
无论是在联想浏览器,edge浏览器,还是谷歌浏览器,打开的页面效果均无遮挡,界面布局,交互体验是一致的。不同环境下均可运行。

总结:网页聊天程序的基本功能是可以正常运行的,流程能够正确执行。需要优化:在高并发下查询历史消息的效率极低,可以引入redis缓存进行缓解。交互体验良好:所有页面内容均能正常显示,无错别字,遮挡,异常。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐