ideaPool论坛系统测试报告

ideaPool论坛系统测试报告

一、项目背景

这是一个基于 Spring Boot、MyBatis、MySQLTabler 前端框架构建 的轻量级论坛系统。提供了用户注册登录、文章发布管理、板块分类、回复互动、站内信 等核心论坛功能,由本人独立完成设计、开发、测试及云服务器部署的全流程工作

1.1 项目简介

模块设计

  • 用户系统: 支持用户注册、登录(Session 管理)、信息修改、头像上传。
  • 文章管理: 支持创建、搜索、修改、删除、点赞文章,按板块分类浏览。
  • 回复系统: 支持对文章进行回复、查看回复列表。
  • 板块管理: 支持多板块分类展示。
  • 站内信: 支持用户之间发送私信、查看未读消息。

技术栈

  • 后端: Spring Boot 3.5.x, MyBatis
  • 数据库: MySQL 8.0+
  • 前端: HTML, JavaScript, Tabler CSS
  • 工具: Lombok, MD5 加密, Maven
  • API 文档: 集成了 Spring Doc (Swagger UI),方便接口调试

1.2 项目描述

应用技术

  • 后端:Spring,Spring Boot,Spring MVC,MyBatis
  • 数据库:MySQL
  • 前端:HTML,CSS,JavaScript,Bootstrap,jQuery3
  • 统一接口返回格式与全局错误定义,标准化前后端交互;
  • 基于@ControllerAdvice实现全局异常拦截,规范异常响应;
  • 拦截器校验用户登录态,强化系统权限安全;
  • MybatisGenerator 自动生成 CRUD 代码,提升数据层开发效率;
  • 集成 Swagger 实现 API 文档自动化与在线测试;
  • 数据库索引优化 + 查询计划分析,提升查询性能与系统响应速度。

1.3 项目结构

复制代码
forum/
├── src/main/java/com/hr/forum/
│   ├── common/          # 公共返回体、状态码
│   ├── config/          # WebMvc、OpenAPI、Session配置
│   ├── controller/      # REST API 控制器
│   ├── dao/             # MyBatis Mapper 接口
│   ├── exception/       # 全局异常处理
│   ├── interceptor/     # 登录拦截器
│   ├── model/           # 实体类
│   ├── services/        # 业务逻辑层
│   └── utils/           # 工具类 (MD5, UUID)
├── src/main/resources/
│   ├── application.yml  # 核心配置
│   ├── mapper/          # MyBatis XML 映射文件
│   └── static/          # 前端页面 (HTML)
└── forum_db.sql         # 数据库初始化脚本

二、项目模块及实现功能

  1. 注册
  2. 登陆模块
  3. 板块模块
  • 获取首页板块列表
  • 获取某一板块列表
  1. 文章模块
  • 发布新贴
  • 搜索帖子
  • 获取帖子列表
  • 帖子详情
  • 修改帖子
  • 点赞
  • 删除帖子
  • 获取用户的帖子列表
  1. 回复模块
  • 回复帖子
  • 获取回复列表
  1. 站内信模块
  • 发送站内信
  • 获取未读消息个数
  • 查询用户的所有站内信
  • 更新为已读
  • 回复站内信
  1. 用户系统模块
  • 修改用户名、电话号码、邮箱、个人简介
  • 修改密码
  • 上传头像

三、测试目标及测试任务概括

验证论坛系统各个模块功能完整性,以及确保用户登录功能、用户注册功能、首页、板块、文章、回复、站内信、用户系统等核心模块符合要求。

四、测试文档

类别
硬件 华硕天选3
手动测试浏览器 Edge 版本 144.0.3719.115 (64 位)
开发工具 IntelliJ IDEA 2024.1.5
自动化测试工具 Selenium 4.21.0
操作系统 Windows 11 家庭中文版
自动化测试浏览器 Chrome 版本 144.0.7559.133(64 位)

五、测试用例

六、自动化测试

6.1 公共类 Utils

  1. 创建驱动createDriver()
  2. 构造函数(页面初始化)Utils(String url)
  3. 屏幕截图getScreenshot(String str)
  4. 登录封装login()
java 复制代码
package com.hr.forum.integration.ui.common;

import io.github.bonigarcia.wdm.WebDriverManager;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.By;
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 org.openqa.selenium.support.ui.WebDriverWait;

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

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 29113
 * Date: 2026-02-08
 * Time: 13:42
 */
public class Utils {
    //获取驱动对象
    public static WebDriver driver=null;
    public WebDriverWait wait=null;
    public static String detailUrl="http://47.98.40.153:8080/sign-in.html";
    public Utils(String url){
        //调用driver对象
        driver=createDriver();
        driver.get(url);

        wait=new WebDriverWait(driver, Duration.ofSeconds(5));
    }
    public static WebDriver createDriver(){
        if(driver==null){
            //下载驱动
            WebDriverManager.chromedriver().setup();
            //添加配置:允许访问所有的连接
            ChromeOptions options=new ChromeOptions();
            options.addArguments("--remote-allow-origins=*");
            //创建驱动对象
            driver=new ChromeDriver(options);
            //隐式等待 3秒 只等待页面元素
            driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(6));

        }
        return driver;
    }
    //屏幕截图
    //屏幕截图文件目录 ./src/test/java/images/
    //                                     /2025-12-22/
    //                                                /test01-195501.png
    //                                                /test02-195501.png

    //                                     /2025-12-23/
    //                                                /test01-195501.png
    // str:方法名/类名
    public void getScreenshot(String str) throws IOException {
        //年月日
        SimpleDateFormat sim1=new SimpleDateFormat("yyyy-MM-dd");
        //时分秒
        SimpleDateFormat sim2=new SimpleDateFormat("HHmmssSS");
        String dayTime=sim1.format(System.currentTimeMillis());
        String fileTime=sim2.format(System.currentTimeMillis());
        //图片文件名:路径+图片名称
        // ./src/test/java/images/2025-12-22/test01-195501.png
        String fileName="./src/test/java/com/hr/forum/integration/ui/images/"+dayTime+"/"+str+"-"+fileTime+".png";

        File srcFile=((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
        FileUtils.copyFile(srcFile,new File(fileName));

    }
    // 封装登录方法
    public static void login() throws InterruptedException {
        // 刷新页面,清空输入框
        driver.navigate().refresh();
        // 用户名
        driver.findElement(By.cssSelector("#username")).sendKeys("滑板社长");
        // 密码
        driver.findElement(By.cssSelector("#password")).sendKeys("123456");
        // 点击登录按钮
        driver.findElement(By.cssSelector("#submit")).click();
        Thread.sleep(500);
        // 通过获取论坛页面logo元素来判断登录是否成功
        driver.findElement(By.cssSelector("body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > h1 > a > img"));
        System.out.println("登录成功");
    }
    //销毁driver对象-关闭浏览器
    public static void quit(){
        if (driver!=null){
            driver.quit();
        }
    }
}

6.2 注册界面 RegisterPageTest

RegisterPageError01():用户名空注册校验(异常注册)
RegisterPageError02():昵称空注册校验(异常注册)
RegisterPageError03():密码空注册校验(异常注册)
RegisterPageError04():密码不一致注册校验(异常注册)
RegisterPageError05():用户名已经被注册(异常注册)
RegisterPageSuccess():成功注册(正常注册)

java 复制代码
package com.hr.forum.integration.ui.tests;

import com.hr.forum.integration.ui.common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.support.ui.ExpectedConditions;

import java.io.IOException;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 29113
 * Date: 2026-02-08
 * Time: 13:49
 *//**
 * 注册页面测试类
 */

public class RegisterPageTest  extends Utils {
    public static String url="http://47.98.40.153:8080/sign-up.html";
    public RegisterPageTest() {
        super(url);
    }

     //检查注册页面是否正常显示
    public void RegisterPageRight(){
        // 通过检查页面元素来判断页面是否正常显示

        // logo图标
        driver.findElement(By.cssSelector("body > div > div > div.text-center.mb-4 > img"));


        // 输入框
        driver.findElement(By.cssSelector("#username"));

        System.out.println("页面正常显示");

    }
    //
    // 异常注册01: 用户名空注册校验
    public void RegisterPageError01() throws IOException, InterruptedException {
        // 刷新页面,清空输入框
        driver.navigate().refresh();
        // 输入昵称,密码,密码重复,勾选同意协议,点击注册
        driver.findElement(By.cssSelector("#nickname")).sendKeys("自动化测试1");
        driver.findElement(By.cssSelector("#password")).sendKeys("123456");
        driver.findElement(By.cssSelector("#passwordRepeat")).sendKeys("123456");
        driver.findElement(By.cssSelector("#policy")).click();
        driver.findElement(By.cssSelector("#submit")).click();
        //获取异常文本
        String Error01 = driver.findElement(By.cssSelector("#signUpForm > div > div:nth-child(2) > div")).getText();
        Thread.sleep(500);
        //截图
        getScreenshot(getClass().getName());
        // 打印异常信息
        System.out.println(Error01);
        assert Error01.equals("用户名不能为空");

    }
    // 异常注册02: 昵称空注册校验
    public void RegisterPageError02() throws IOException, InterruptedException {
        // 刷新页面,清空输入框
        driver.navigate().refresh();
        // 输入用户名,密码,密码重复,勾选同意协议,点击注册
        driver.findElement(By.cssSelector("#username")).sendKeys("自动化测试5");
        driver.findElement(By.cssSelector("#password")).sendKeys("123456");
        driver.findElement(By.cssSelector("#passwordRepeat")).sendKeys("123456");
        driver.findElement(By.cssSelector("#policy")).click();

        driver.findElement(By.cssSelector("#submit")).click();
        //获取异常文本
        String Error02 = driver.findElement(By.cssSelector("#signUpForm > div > div:nth-child(3) > div")).getText();
        Thread.sleep(500);
        //截图
        getScreenshot(getClass().getName());
        // 打印异常信息
        System.out.println(Error02);
        assert Error02.equals("昵称不能为空");
    }
    // 异常注册03: 密码空注册校验
    public void RegisterPageError03() throws IOException, InterruptedException {
        // 刷新页面,清空输入框
        driver.navigate().refresh();
        // 输入用户名,昵称,确认密码,勾选同意协议,点击注册
        driver.findElement(By.cssSelector("#username")).sendKeys("自动化测试5");
        driver.findElement(By.cssSelector("#nickname")).sendKeys("自动化测试1");
        driver.findElement(By.cssSelector("#passwordRepeat")).sendKeys("123456");
        driver.findElement(By.cssSelector("#policy")).click();
        driver.findElement(By.cssSelector("#submit")).click();
        //获取异常文本
        String Error03 = driver.findElement(By.cssSelector("#signUpForm > div > div:nth-child(4) > div > div")).getText();
        Thread.sleep(500);
        //截图
        getScreenshot(getClass().getName());
        // 打印异常信息
        System.out.println(Error03);
        assert Error03.equals("密码不能为空");
    }
    // 异常注册04: 密码不一致注册校验
    public void RegisterPageError04() throws IOException, InterruptedException {
        // 刷新页面,清空输入框
        driver.navigate().refresh();
        // 输入用户名,昵称,密码和确认密码(不一致),勾选同意协议,点击注册
        driver.findElement(By.cssSelector("#username")).sendKeys("自动化测试5");
        driver.findElement(By.cssSelector("#nickname")).sendKeys("自动化测试1");
        driver.findElement(By.cssSelector("#password")).sendKeys("123456");
        driver.findElement(By.cssSelector("#passwordRepeat")).sendKeys("1234567");
        driver.findElement(By.cssSelector("#policy")).click();
        driver.findElement(By.cssSelector("#submit")).click();
        //获取异常文本
        String Error04 = driver.findElement(By.cssSelector("#signUpForm > div > div:nth-child(5) > div ")).getText();
        Thread.sleep(500);
        //截图
        getScreenshot(getClass().getName());
        // 打印异常信息
        System.out.println(Error04);
        assert Error04.equals("请检查确认密码");
        Utils.quit();
    }
    // 异常注册05: 用户名已经被注册
    public void RegisterPageError05() throws IOException, InterruptedException {
        // 刷新页面,清空输入框
        driver.navigate().refresh();
        // 输入相同用户名,昵称,密码,密码重复,勾选同意协议,点击注册
        driver.findElement(By.cssSelector("#username")).sendKeys("自动化测试1");
        driver.findElement(By.cssSelector("#nickname")).sendKeys("自动化测试1");
        driver.findElement(By.cssSelector("#password")).sendKeys("123456");
        driver.findElement(By.cssSelector("#passwordRepeat")).sendKeys("123456");
        driver.findElement(By.cssSelector("#policy")).click();
        driver.findElement(By.cssSelector("#submit")).click();
        // 显性等待
        By toastLocator = By.cssSelector("div.jq-toast-single.jq-icon-warning:not([style*='display: none'])");
        wait.until(ExpectedConditions.visibilityOfElementLocated(toastLocator));
        //获取异常文本
        String Error05 = driver.findElement(By.cssSelector(".jq-toast-heading")).getText();
        Thread.sleep(500);
        //截图
        getScreenshot(getClass().getName());
        // 打印异常信息
        System.out.println(Error05);
        assert Error05.equals("注册失败");
    }

    // 成功注册
    public void RegisterPageSuccess() throws IOException {
        // 刷新页面,清空输入框
        driver.navigate().refresh();
        // 输入用户名,昵称,密码,密码重复,勾选同意协议,点击注册
        driver.findElement(By.cssSelector("#username")).sendKeys("自动化测试5");
        driver.findElement(By.cssSelector("#nickname")).sendKeys("自动化测试1");
        driver.findElement(By.cssSelector("#password")).sendKeys("123456");
        driver.findElement(By.cssSelector("#passwordRepeat")).sendKeys("123456");
        driver.findElement(By.cssSelector("#policy")).click();
        driver.findElement(By.cssSelector("#submit")).click();

        // 注册成功跳转登录页面
        // 通过检查弹窗和url是否注册成功并跳转至登录界面
        Alert alert = wait.until(ExpectedConditions.alertIsPresent());// 显示等待
        alert.accept();
        // 截图
        getScreenshot(getClass().getName());
        //获取当前页面url
        String currentUrl = driver.getCurrentUrl();
        //如果已经跳转至登录界面, 则表示注册成功
        assert currentUrl.equals("http://47.98.40.153:8080/sign-up.html");

    }
}

6.3 登陆界面 LoginPageTest

LoginPageRight():检查登录页面是否正常显示
LoginPageError01():用户名为空登录校验(登录异常)
LoginPageError02():密码为空登录校验(登录异常)
LoginPageError03():用户名错误登录校验(登录异常)
LoginPageError04():密码错误登录校验(登录异常)
LoginPageSuccess():登录正常校验

java 复制代码
package com.hr.forum.integration.ui.tests;

import com.hr.forum.integration.ui.common.Utils;
import org.openqa.selenium.By;
import org.openqa.selenium.support.ui.ExpectedConditions;

import java.io.IOException;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 29113
 * Date: 2026-02-08
 * Time: 16:22
 */

/**
 * 登录页面测试类
 */
public class LoginPageTest extends Utils {
    public static String url="http://47.98.40.153:8080/sign-in.html";
    public LoginPageTest() {
        super(url);
    }
    // 检查登录页面是否正常显示
    public void LoginPageRight(){
        // 通过检查页面元素来判断页面是否正常显示

        // logo图标
        driver.findElement(By.cssSelector("body > div > div > div > div:nth-child(1) > div > div.text-center.mb-4 > img"));


        // 输入框
        driver.findElement(By.cssSelector("#username"));

        System.out.println("页面正常显示");

    }
    // 登录异常01: 用户名为空登录校验
    public void LoginPageError01() throws IOException, InterruptedException {
        // 刷新页面,清空输入框
        driver.navigate().refresh();

        // 密码
        driver.findElement(By.cssSelector("#password")).sendKeys("123456");
        // 点击登录按钮
        driver.findElement(By.cssSelector("#submit")).click();
        //获取异常文本
        String Error01 = driver.findElement(By.cssSelector("#signInForm > div.mb-3 > div")).getText();
        Thread.sleep(500);
        //截图
        getScreenshot(getClass().getName());
        // 打印异常信息
        System.out.println(Error01);
        assert Error01.equals("用户名不能为空");

    }
    // 登录异常02: 密码为空登录校验
    public void LoginPageError02() throws IOException, InterruptedException {
        // 刷新页面,清空输入框
        driver.navigate().refresh();

        // 用户名
        driver.findElement(By.cssSelector("#username")).sendKeys("自动化测试1");
        // 点击登录按钮
        driver.findElement(By.cssSelector("#submit")).click();
        //获取异常文本
        String Error01 = driver.findElement(By.cssSelector("#signInForm > div.mb-3 > div")).getText();
        Thread.sleep(500);
        //截图
        getScreenshot(getClass().getName());
        // 打印异常信息
        System.out.println(Error01);
        assert Error01.equals("密码不能为空");

    }
    // 登录异常03: 用户名错误登录校验
    public void LoginPageError03() throws IOException, InterruptedException {
        // 刷新页面,清空输入框
        driver.navigate().refresh();
        // 用户名
        driver.findElement(By.cssSelector("#username")).sendKeys("自动化测试55");
        // 密码
        driver.findElement(By.cssSelector("#password")).sendKeys("1234567");
        // 点击登录按钮
        driver.findElement(By.cssSelector("#submit")).click();
        // 显性等待 jq-toast-single jq-has-icon jq-icon-warning
        By toastLocator = By.cssSelector("div.jq-toast-single.jq-icon-warning:not([style*='display: none'])");
        wait.until(ExpectedConditions.visibilityOfElementLocated(toastLocator));
        //获取异常文本
        String Error05 = driver.findElement(By.cssSelector(".jq-toast-heading")).getText();
        Thread.sleep(500);
        //截图
        getScreenshot(getClass().getName());
        // 打印异常信息
        System.out.println(Error05);
        assert Error05.equals("登录失败");
    }
    // 登录异常04: 密码错误登录校验
    public void LoginPageError04() throws IOException, InterruptedException {
        // 刷新页面,清空输入框
        driver.navigate().refresh();
        // 用户名
        driver.findElement(By.cssSelector("#username")).sendKeys("自动化测试1");
        // 密码
        driver.findElement(By.cssSelector("#password")).sendKeys("<PASSWORD>");
        // 点击登录按钮
        driver.findElement(By.cssSelector("#submit")).click();
        // 显性等待 jq-toast-single jq-has-icon jq-icon-warning
        By toastLocator = By.cssSelector("div.jq-toast-single.jq-icon-warning:not([style*='display: none'])");
        wait.until(ExpectedConditions.visibilityOfElementLocated(toastLocator));
        //获取异常文本
        String Error05 = driver.findElement(By.cssSelector(".jq-toast-heading")).getText();
        Thread.sleep(500);
        //截图
        getScreenshot(getClass().getName());
        // 打印异常信息
        System.out.println(Error05);
        assert Error05.equals("登录失败");
    }
    // 登录正常校验
    public void LoginPageSuccess() throws IOException, InterruptedException {
        // 刷新页面,清空输入框
        driver.navigate().refresh();
        // 用户名
        driver.findElement(By.cssSelector("#username")).sendKeys("滑板社长");
        // 密码
        driver.findElement(By.cssSelector("#password")).sendKeys("123456");
        // 点击登录按钮
        driver.findElement(By.cssSelector("#submit")).click();
        Thread.sleep(500);
        // 通过获取论坛页面logo元素来判断登录是否成功
        driver.findElement(By.cssSelector("body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > h1 > a > img"));
        System.out.println("登录成功");
        // 截图
        getScreenshot(getClass().getName());
        // 退出
        Utils.quit();


    }
}

6.4 论坛首页 HomePageTest

HomePageSearch():菜单栏搜索框功能01-输入存在的帖子标题关键词,验证搜索结果列表包含该帖子
HomePageSearchError():菜单栏搜索框功能02-输入不存在的帖子标题关键词,验证搜索结果列表为"还没有帖子"
HomePageChangeBoard():导航栏板块切换功能01-板块切换验证
HomePageChangeTheme():菜单栏主题切换功能
HomePageMyArtical():菜单栏我的帖子
HomePageMyCenter():菜单栏个人中心
HomePageNotice():菜单栏通知查看功能
PublishArticle():发布帖子功能
ArticleJump():帖子跳转功能

java 复制代码
package com.hr.forum.integration.ui.tests;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 29113
 * Date: 2026-02-08
 * Time: 18:46
 */

import com.hr.forum.integration.ui.common.Utils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;

import java.io.IOException;
import java.time.Duration;

/**
 * 论坛首页测试类
 */
public class HomePageTest extends Utils {
    public static String url="http://47.98.40.153:8080/index.html";

    public HomePageTest() {
        super(url);
    }
    // 菜单栏搜索框功能01-输入存在的帖子标题关键词,验证搜索结果列表包含该帖子
    public void HomePageSearch() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(2000));
        // 输入搜索关键词
        driver.findElement(By.cssSelector("#index_search_input")).sendKeys("永劫无间小技巧");
        // 通过测试搜索到的帖子标题,判断搜索功能是否正常
        String title = driver.findElement(By.cssSelector("#artical-items-body > div > div > div.col > div.text-truncate > a > strong")).getText();
        //截图
        getScreenshot(getClass().getName());
        // 打印搜索结果
        System.out.println(title);
        assert title.equals("永劫无间小技巧");
    }
    // 菜单栏搜索框功能02-输入不存在的帖子标题关键词,验证搜索结果列表为"还没有帖子"
    public void HomePageSearchError() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(2000));
        // 输入搜索关键词
        driver.findElement(By.cssSelector("#index_search_input")).sendKeys("123456");
        // 通过测试搜索到的是否为"还没有帖子"来判断
        String title = driver.findElement(By.cssSelector("#artical-items-body")).getText();
        //截图
        getScreenshot(getClass().getName());
        // 打印搜索结果
        System.out.println(title);
        assert title.equals("还没有帖子");
    }
    //导航栏板块切换功能01-板块切换验证
    public void HomePageChangeBoard() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(2000));
        // 点击'Java'板块切换按钮
        driver.findElement(By.cssSelector("#topBoardList > li:nth-child(2) > a > span.nav-link-title")).click();
        Thread.sleep(500);
        // 获取板块标题
        String title = driver.findElement(By.cssSelector("#article_list_board_title")).getText();
        //截图
        getScreenshot(getClass().getName());
        // 打印板块标题
        System.out.println(title);
        // 验证板块切换功能是否正常
        assert title.equals("Java");

    }
    // 菜单栏主题切换功能
    public void HomePageChangeTheme() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(2000));
        // 点击'深色'主题切换按钮
        driver.findElement(By.cssSelector("body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-dark > svg")).click();
        // 通过查看url来判断切换功能是否正常
        String url = driver.getCurrentUrl();
        // 截图
        getScreenshot(getClass().getName());
        // 打印url
        System.out.println(url);
        // 验证主题切换功能是否正常
        assert url.equals("http://47.98.40.153:8080/index.html?theme=dark");


    }
    // 菜单栏我的帖子
    public void HomePageMyArtical() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(5000));
        // 先获得个人获取昵称
        String nickNameBe = driver.findElement(By.cssSelector("#index_nav_nickname")).getAttribute("innerHTML");
        // 点击菜单栏个人头像
        driver.findElement(By.cssSelector("#index_nav_avatar")).click();
        // 再点击我的帖子
        driver.findElement(By.cssSelector("#index_user_profile")).click();
        // 通过查看我的帖子中昵称是否是本人来判断跳转功能是否正常
        // 用Lambda 显性等待,等待我的帖子页昵称元素渲染完成
        String nicKNameAf = wait.until(driver -> {
            String innerHTML = driver.findElement(By.cssSelector("#profile_nickname")).getText().trim();
            return !innerHTML.isEmpty() ? innerHTML : null;
        });
        // 截图
        getScreenshot(getClass().getName());
        // 打印昵称
        System.out.println(nicKNameAf);
        // 验证跳转功能是否正常
        assert nickNameBe.equals(nicKNameAf);
        Utils.quit();
    }
    // 菜单栏个人中心
    public void HomePageMyCenter() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 获取昵称
//        By nickNameLo = By.cssSelector("#index_nav_nickname");
//        WebElement nickNam =  wait.until(ExpectedConditions.visibilityOfElementLocated(nickNameLo));
//        String nickNameBe = nickNam.getText();
        // getAttribute("textContent"); 元素隐藏但有文本、动态渲染的文本、getText () 返回空时使用
        String nickNameBe = driver.findElement(By.cssSelector("#index_nav_nickname")).getAttribute("textContent");
        System.out.println(nickNameBe);
        // 点击菜单栏个人头像
        By avatarLoc = By.cssSelector("#index_nav_avatar");
        WebElement avatar =  wait.until(ExpectedConditions.elementToBeClickable(avatarLoc));
        avatar.click();
        // 点击个人中心
        By myCenterLoc = By.cssSelector("#index_user_settings");
        WebElement myCenter =  wait.until(ExpectedConditions.elementToBeClickable(myCenterLoc));
        myCenter.click();
        // 通过查看个人中心昵称是否是本人来判断跳转功能是否正常
        By nickNameLoc = By.cssSelector("#settings_nickname");
        WebElement nickName =  wait.until(ExpectedConditions.elementToBeClickable(nickNameLoc));
        String nickNameAf = nickName.getText();
        // 截图
        getScreenshot(getClass().getName());
        if (nickNameBe.equals(nickNameAf)){
            System.out.println("跳转功能正常");
        }else {
            System.out.println("跳转功能异常");
        }
        Utils.quit();
    }
    // 菜单栏通知查看功能
    public void HomePageNotice() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(5000));
        // 点击菜单栏通知按钮
        driver.findElement(By.cssSelector("body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > div > a > svg")).click();
        // 通过获取站内信标题来判断跳转功能是否正常
        // 用Lambda 显性等待,等待通知栏站内信昵称元素渲染完成
        String title = wait.until(driver -> {
            String innerHTML = driver.findElement(By.cssSelector("#offcanvasEndLabel")).getText().trim();
            return !innerHTML.isEmpty() ? innerHTML : null;
        });
        // 截图
        getScreenshot(getClass().getName());
        // 打印标题
        System.out.println(title);
        assert title.equals("所有站内信");
        Utils.quit();
    }
    // 发布帖子功能
    public void PublishArticle() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(5000));
        // 点击发布按钮
        driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div > div.col-auto.ms-auto.d-print-none > div > a.btn.btn-primary.d-none.d-sm-inline-block.article_post")).click();
        Thread.sleep(1000);
        // 通过获取发贴页面的"发新贴"来判断跳转功能是否正常
        // 用Lambda 显性等待,等待发贴页面的"发新贴"元素渲染完成
        String title = wait.until(driver -> {
            String innerHTML = driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div > div > h2")).getText().trim();
            return !innerHTML.isEmpty() ? innerHTML : null;
        });
        // 截图
        getScreenshot(getClass().getName());
        // 打印标题
        System.out.println(title);
        assert title.equals("发新贴");
        Utils.quit();
    }
    // 帖子跳转功能
    public void ArticleJump() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(5000));
        // 获取帖子标题
        String titleBe = driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong")).getText();
        // 点击帖子标题
        driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong")).click();
        // 通过点击后的获取页面对的帖子标题是否相同判断跳转功能是否正常
        String titleAf = wait.until(driver -> {
            String innerHTML = driver.findElement(By.cssSelector("#details_article_content_title")).getText().trim();
            return !innerHTML.isEmpty() ? innerHTML : null;
        });
        // 截图
        getScreenshot(getClass().getName());
        // 打印标题
        System.out.println(titleAf);
        assert titleAf.equals(titleBe);
        // 返回
        Utils.quit();

    }
}

6.5 发帖界面 PublishPageTest

PublishPage():测试是否能正常进入发布帖子界面
PublishArticleSuccess():正常发帖
PublishArticleError01():发帖标题为空(异常发帖)
PublishArticleError02():发帖内容为空(异常发帖)
PublishArticleError03():发帖内容和标题都为空(异常发帖)

java 复制代码
package com.hr.forum.integration.ui.tests;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 29113
 * Date: 2026-02-09
 * Time: 14:27
 */

import com.hr.forum.integration.ui.common.Utils;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedConditions;

import java.io.IOException;
import java.time.Duration;

/**
 * 发帖页面测试类
 */
public class PublishPageTest extends Utils {
 public static String url="http://47.98.40.153:8080/index.html";

    public PublishPageTest() {
        super(url);
    }
    // 测试是否能正常进入发布帖子界面
    public void PublishPage() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(5000));
        // 点击"发布帖子"按钮
        driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div > div.col-auto.ms-auto.d-print-none > div > a.btn.btn-primary.d-none.d-sm-inline-block.article_post")).click();
        Thread.sleep(1000);
        // 通过获取发贴页面的"发新贴"来判断跳转功能是否正常
        String title = driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div > div > h2")).getText();
        // 截图
        getScreenshot(getClass().getName());
        if (title.equals("发新贴")) {
            System.out.println("跳转成功");
        } else {
            System.out.println("跳转失败");
        }
        driver.quit();

    }
    // 正常发帖
    public void PublishArticleSuccess() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(5000));
        // 点击"发布帖子"按钮,进入发帖页面
        driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div > div.col-auto.ms-auto.d-print-none > div > a.btn.btn-primary.d-none.d-sm-inline-block.article_post")).click();
        Thread.sleep(1000);
        // 点击"板块"选框
        driver.findElement(By.cssSelector("#article_post_borad")).click();
        // 板块选框选择"前端技术"板块
        driver.findElement(By.cssSelector("#article_post_borad > option:nth-child(3)")).click();
        // 输入标题
        driver.findElement(By.cssSelector("#article_post_title")).click();
        driver.findElement(By.cssSelector("#article_post_title")).sendKeys("自动化输入内容");
        // 输入内容
        // 由于特殊可编辑元素(比如 CodeMirror 编辑器) 的输入操作,核心是通过 Actions 类模拟 "鼠标点击 + 键盘输入" 的人工操作流程
        // 1. 定位到  编辑器的输入行元素
        WebElement ele = driver.findElement(By.cssSelector("#edit-article > div.CodeMirror.cm-s-default.CodeMirror-wrap.CodeMirror-empty"));
        // 2. 用 Actions 模拟人工操作:先点击这个元素激活输入框,再输入"自动化输入内容",最后执行这个操作序列
        new Actions(driver).click(ele).sendKeys("自动化输入内容").perform();
        // 点击"发布"按钮,由于元素不在可视区域外,所以先滚动页面,让元素进入可视区域
        // 1. 定位目标元素
        By targetLoc = By.cssSelector("#article_post_submit");
        WebElement targetEle = driver.findElement(targetLoc);
        // 2. 滚动页面到元素可见位置
        ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", targetEle);
        // 加短暂等待,确保滚动完成
        Thread.sleep(500);
        // 3. 再执行点击
        new Actions(driver).click(targetEle).perform();
        // 通过获取发帖成功页面的"发帖成功"提示来判断发帖功能是否正常
        // 显性等待 jq-toast-single jq-has-icon jq-icon-success
        By toastLocator = By.cssSelector("div.jq-toast-single.jq-icon-success:not([style*='display: none'])");
        wait.until(ExpectedConditions.visibilityOfElementLocated(toastLocator));
        // 获取成功提示
        String Success = driver.findElement(By.cssSelector(".jq-toast-heading")).getText();
        // 截图
        getScreenshot(getClass().getName());
        if (Success.equals("成功")) {
            System.out.println("发帖成功");
        } else {
            System.out.println("发帖失败");
        }
        // 退出
        Utils.quit();
    }
    // 异常发帖01:发帖标题为空
    public void PublishArticleError01() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(5000));
        // 点击"发布帖子"按钮,进入发帖页面
        driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div > div.col-auto.ms-auto.d-print-none > div > a.btn.btn-primary.d-none.d-sm-inline-block.article_post")).click();
        Thread.sleep(1000);
        // 点击"板块"选框
        driver.findElement(By.cssSelector("#article_post_borad")).click();
        // 板块选框选择"前端技术"板块
        driver.findElement(By.cssSelector("#article_post_borad > option:nth-child(3)")).click();
        // 输入内容
        // 由于特殊可编辑元素(比如 CodeMirror 编辑器) 的输入操作,核心是通过 Actions 类模拟 "鼠标点击 + 键盘输入" 的人工操作流程
        // 1. 定位到  编辑器的输入行元素
        WebElement ele = driver.findElement(By.cssSelector("#edit-article > div.CodeMirror.cm-s-default.CodeMirror-wrap.CodeMirror-empty"));
        // 2. 用 Actions 模拟人工操作:先点击这个元素激活输入框,再输入"自动化输入内容",最后执行这个操作序列
        new Actions(driver).click(ele).sendKeys("自动化输入内容").perform();
        // 点击"发布"按钮,由于元素不在可视区域外,所以先滚动页面,让元素进入可视区域
        // 1. 定位目标元素
        By targetLoc = By.cssSelector("#article_post_submit");
        WebElement targetEle = driver.findElement(targetLoc);
        // 2. 滚动页面到元素可见位置
        ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", targetEle);
        // 加短暂等待,确保滚动完成
        Thread.sleep(500);
        // 3. 再执行点击
        new Actions(driver).click(targetEle).perform();
        // 通过获取发帖页面的"提示"提示来判断发帖功能是否正常
        // 显性等待 jq-toast-single jq-has-icon jq-icon-warning
        By toastLocator = By.cssSelector("div.jq-toast-single.jq-icon-warning:not([style*='display: none'])");
        wait.until(ExpectedConditions.visibilityOfElementLocated(toastLocator));
        // 获取成功提示
        String Success = driver.findElement(By.cssSelector(".jq-toast-heading")).getText();
        // 截图
        getScreenshot(getClass().getName());
        if (Success.equals("请输入帖子标题")) {
            System.out.println("发帖成功");
        } else {
            System.out.println("发帖失败,验证帖子标题为空");
        }
        // 退出
        Utils.quit();
    }
    // 异常发帖02:发帖内容为空
    public void PublishArticleError02() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(5000));
        // 点击"发布帖子"按钮,进入发帖页面
        driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div > div.col-auto.ms-auto.d-print-none > div > a.btn.btn-primary.d-none.d-sm-inline-block.article_post")).click();
        Thread.sleep(1000);
        // 点击"板块"选框
        driver.findElement(By.cssSelector("#article_post_borad")).click();
        // 板块选框选择"前端技术"板块
        driver.findElement(By.cssSelector("#article_post_borad > option:nth-child(3)")).click();
        // 输入标题
        driver.findElement(By.cssSelector("#article_post_title")).click();
        driver.findElement(By.cssSelector("#article_post_title")).sendKeys("自动化输入内容");
        // 点击"发布"按钮,由于元素不在可视区域外,所以先滚动页面,让元素进入可视区域
        // 1. 定位目标元素
        By targetLoc = By.cssSelector("#article_post_submit");
        WebElement targetEle = driver.findElement(targetLoc);
        // 2. 滚动页面到元素可见位置
        ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", targetEle);
        // 加短暂等待,确保滚动完成
        Thread.sleep(500);
        // 3. 再执行点击
        new Actions(driver).click(targetEle).perform();
        // 通过获取发帖页面的"提示"提示来判断发帖功能是否正常
        // 显性等待 jq-toast-single jq-has-icon jq-icon-warning
        By toastLocator = By.cssSelector("div.jq-toast-single.jq-icon-warning:not([style*='display: none'])");
        wait.until(ExpectedConditions.visibilityOfElementLocated(toastLocator));
        // 获取成功提示
        String Success = driver.findElement(By.cssSelector(".jq-toast-heading")).getText();
        // 截图
        getScreenshot(getClass().getName());
        if (Success.equals("请输入帖子内容")) {
            System.out.println("发帖成功");
        } else {
            System.out.println("发帖失败,验证帖子内容为空");
        }
        // 退出
        Utils.quit();
    }
    // 异常发帖03: 发帖内容和标题都为空
    public void PublishArticleError03() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(5000));
        // 点击"发布帖子"按钮,进入发帖页面
        driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div > div.col-auto.ms-auto.d-print-none > div > a.btn.btn-primary.d-none.d-sm-inline-block.article_post")).click();
        Thread.sleep(1000);
        // 点击"板块"选框
        driver.findElement(By.cssSelector("#article_post_borad")).click();
        // 板块选框选择"前端技术"板块
        driver.findElement(By.cssSelector("#article_post_borad > option:nth-child(3)")).click();
        // 点击"发布"按钮,由于元素不在可视区域外,所以先滚动页面,让元素进入可视区域
        // 1. 定位目标元素
        By targetLoc = By.cssSelector("#article_post_submit");
        WebElement targetEle = driver.findElement(targetLoc);
        // 2. 滚动页面到元素可见位置
        ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", targetEle);
        // 加短暂等待,确保滚动完成
        Thread.sleep(500);
        // 3. 再执行点击
        new Actions(driver).click(targetEle).perform();
        // 通过获取发帖页面的"提示"提示来判断发帖功能是否正常
        // 显性等待 jq-toast-single jq-has-icon jq-icon-warning
        By toastLocator = By.cssSelector("div.jq-toast-single.jq-icon-warning:not([style*='display: none'])");
        wait.until(ExpectedConditions.visibilityOfElementLocated(toastLocator));
        // 获取成功提示
        String Success = driver.findElement(By.cssSelector(".jq-toast-heading")).getText();
        // 截图
        getScreenshot(getClass().getName());
        if (Success.equals("请输入帖子标题")) {
            System.out.println("发帖成功");
        } else {
            System.out.println("发帖失败,验证帖子内容和标题为空");
        }
        // 退出
        Utils.quit();

    }
}

6.6 帖子详情 DetailsPageTest

testDetailsPage():测试是否能正常进入帖子详情界面
testLike():测试帖子详情点赞功能
testReply():测试回复帖子功能
testEdit():测试在帖子详情页成功编辑

java 复制代码
package com.hr.forum.integration.ui.tests;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 29113
 * Date: 2026-02-09
 * Time: 15:14
 */

import com.hr.forum.integration.ui.common.Utils;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedConditions;

import java.io.IOException;
import java.time.Duration;

/**
 * 帖子详情测试类
 */
public class DetailsPageTest extends Utils {
    public static String url="http://47.98.40.153:8080/index.html";

    public DetailsPageTest() {
        super(url);
    }
    // 测试是否能正常进入帖子详情界面
    public void testDetailsPage() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(5000));
        // 获取帖子标题
        String titleBe = driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong")).getText();
        // 点击帖子标题
        driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong")).click();
        // 通过点击后的获取页面对的帖子标题是否相同判断跳转功能是否正常
        String titleAf = wait.until(driver -> {
            String innerHTML = driver.findElement(By.cssSelector("#details_article_content_title")).getText().trim();
            return !innerHTML.isEmpty() ? innerHTML : null;
        });
        // 截图
        getScreenshot(getClass().getName());
        // 打印标题
        System.out.println(titleAf);
        if (titleAf.equals(titleBe)) {
            System.out.println("跳转成功");
        } else {
            System.out.println("跳转失败");
        }
        driver.quit();
        // 返回
        Utils.quit();
    }
    // 测试帖子详情点赞功能
    public void testLike() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(5000));
        // 点击帖子标题进入帖子详情页面
        driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong")).click();
        // 点击"点赞"按钮
        driver.findElement(By.cssSelector("#details_btn_like_count")).click();
        // 通过获取帖子详情页面的"成功"提示来判断点赞功能是否正常
        // 显性等待 jq-toast-single jq-has-icon jq-icon-success
        By toastLocator = By.cssSelector("div.jq-toast-single.jq-icon-success:not([style*='display: none'])");
        wait.until(ExpectedConditions.visibilityOfElementLocated(toastLocator));
        // 获取成功提示
        String Success = driver.findElement(By.cssSelector(".jq-toast-heading")).getText();
        System.out.println(Success);
        // 截图
        getScreenshot(getClass().getName());
        if (Success.equals("成功")) {
            System.out.println("点赞成功");
        } else {
            System.out.println("点赞失败");
        }
        // 退出
        driver.quit();
    }
    // 测试回复帖子功能
    public void testReply() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(5000));
        // 点击帖子标题进入帖子详情页面
        driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong")).click();
        // 输入回复内容
        // 由于特殊可编辑元素(比如 CodeMirror 编辑器) 的输入操作,核心是通过 Actions 类模拟 "鼠标点击 + 键盘输入" 的人工操作流程
        // 1. 定位到回复编辑器的输入行元素
        WebElement ele = driver.findElement(By.cssSelector("#article_details_reply > div.CodeMirror.cm-s-default.CodeMirror-wrap.CodeMirror-empty > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre"));
        // 2. 用 Actions 模拟人工操作:先点击这个元素激活输入框,再输入"自动化输入内容",最后执行这个操作序列
        new Actions(driver).click(ele).sendKeys("自动化输入回复内容").perform();

        // 点击"回复"按钮,由于元素不在可视区域外,所以先滚动页面,让元素进入可视区域
        // 1. 定位目标元素
        By targetLoc = By.cssSelector("#details_btn_article_reply");
        WebElement targetEle = driver.findElement(targetLoc);
        // 2. 滚动页面到元素可见位置
        ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", targetEle);
        // 加短暂等待,确保滚动完成
        Thread.sleep(500);
        // 3. 再执行点击
        new Actions(driver).click(targetEle).perform();
        // 通过获取帖子详情页面的"成功"提示来判断回复功能是否正常
        // 显性等待 jq-toast-single jq-has-icon jq-icon-success
        By toastLocator = By.cssSelector("div.jq-toast-single.jq-icon-success:not([style*='display: none'])");
        wait.until(ExpectedConditions.visibilityOfElementLocated(toastLocator));
        // 获取成功提示
        String Success = driver.findElement(By.cssSelector(".jq-toast-heading")).getText();
        // 截图
        getScreenshot(getClass().getName());
        if (Success.equals("成功")){
            System.out.println("回复成功");
        }else {
            System.out.println("回复失败");
        }
        driver.quit();

    }
    // 测试在帖子详情页成功编辑
    public void testEdit() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(5000));
        // 点击帖子标题进入帖子详情页面
        driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong")).click();
        // 点击"编辑"按钮
        driver.findElement(By.cssSelector("#details_artile_edit")).click();
        // 输入编辑标题
        driver.findElement(By.cssSelector("#edit_article_title")).clear();
        driver.findElement(By.cssSelector("#edit_article_title")).click();
        driver.findElement(By.cssSelector("#edit_article_title")).sendKeys("自动化测试帖子详情页面编辑帖子标题");
        // 输入编辑内容
        // 由于特殊可编辑元素(比如 CodeMirror 编辑器) 的输入操作,核心是通过 Actions 类模拟 "鼠标点击 + 键盘输入" 的人工操作流程
        // 1. 定位到回复编辑器的输入行元素
        WebElement ele = driver.findElement(By.cssSelector("#edit_article_content_area > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre"));
        // 2. 用 Actions 模拟人工操作:先点击这个元素激活输入框,再输入"自动化输入内容",最后执行这个操作序列
        new Actions(driver).click(ele).sendKeys("自动化测试帖子详情页面编辑帖子内容").perform();
        // 点击"提交"按钮,由于元素不在可视区域外,所以先滚动页面,让元素进入可视区域
        // 1. 定位目标元素
        By targetLoc = By.cssSelector("#edit_article_submit");
        WebElement targetEle = driver.findElement(targetLoc);
        // 2. 滚动页面到元素可见位置
        ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", targetEle);
        // 加短暂等待,确保滚动完成
        Thread.sleep(500);
        // 3. 再执行点击
        new Actions(driver).click(targetEle).perform();
        // 通过获取编辑页面的"成功"提示来判断编辑功能是否正常
        // 显性等待 jq-toast-single jq-has-icon jq-icon-success
        By toastLocator = By.cssSelector("div.jq-toast-single.jq-icon-success:not([style*='display: none'])");
        wait.until(ExpectedConditions.visibilityOfElementLocated(toastLocator));
        // 获取成功提示
        String Success = driver.findElement(By.cssSelector(".jq-toast-heading")).getText();
        // 截图
        getScreenshot(getClass().getName());
        if (Success.equals("成功")){
            System.out.println("编辑成功");
        }else {
            System.out.println("编辑失败");
        }
        driver.quit();

    }

}

6.7 站内信 MessagePageTest

testSendMessage():帖子详细页发私信功能
testMessagePage():测试站内信页列表回复私信功能

java 复制代码
package com.hr.forum.integration.ui.tests;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 29113
 * Date: 2026-02-09
 * Time: 15:53
 */

import com.hr.forum.integration.ui.common.Utils;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedConditions;

import javax.swing.*;
import java.io.IOException;
import java.security.Key;
import java.time.Duration;

/**
 * 站内信页面测试类
 */
public class MessagePageTest extends Utils {
    public static String url="http://47.98.40.153:8080/index.html";

    public MessagePageTest() {
        super(url);
    }
    // 帖子详细页发私信功能
    public void testSendMessage() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(5000));
        // 点击帖子标题进入帖子详情页面
        // 点击帖子标题
        driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong")).click();
        // 点击"私信" 按钮
        driver.findElement(By.cssSelector("#btn_details_send_message")).click();
        // 输入私信内容
        driver.findElement(By.cssSelector("#index_message_receive_content")).sendKeys("测试私信功能");
        Thread.sleep(500);
        // 点击发送按钮
//        By btnLocator = By.cssSelector("#btn_index_send_message");
//        wait.until(ExpectedConditions.elementToBeClickable(btnLocator));
        driver.findElement(By.cssSelector("#btn_index_send_message")).click();
        // 通过获取帖子详情页面的"成功"提示来判断私信功能是否正常
        // 显性等待 jq-toast-single jq-has-icon jq-icon-success
        By toastLocator = By.cssSelector("div.jq-toast-single.jq-icon-success:not([style*='display: none'])");
        wait.until(ExpectedConditions.visibilityOfElementLocated(toastLocator));
        // 获取成功提示
        String Success = driver.findElement(By.cssSelector(".jq-toast-heading")).getText();
        System.out.println(Success);
        // 截图
        getScreenshot(getClass().getName());
        if (Success.equals("成功")) {
            System.out.println("发送成功");
        } else {
            System.out.println("发送失败");
        }
        // 退出
        driver.quit();
    }
    // 测试站内信页列表回复私信功能
    public void testMessagePage() throws InterruptedException, IOException {
        // 登录
        Utils.login();
//        // 隐式等待
//        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(5000));
        // 点击"站内信"按钮
        By messageIconLoc=By.cssSelector("body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > div > a > svg");
        WebElement messageIcon= wait.until(ExpectedConditions.elementToBeClickable(messageIconLoc));
        messageIcon.click();
        System.out.println(" 点击站内信图标成功");

        // 点击消息标签
        By messageLoc=By.cssSelector("#index_div_message_list > div:nth-child(1) > div > div.col.text-truncate > a");
        WebElement message=wait.until(ExpectedConditions.elementToBeClickable(messageLoc));
        message.click();
        System.out.println(" 点击消息标签成功");

        // 点击"回复"按钮
        By replyLoc=By.cssSelector("#btn_index_message_reply");
        WebElement reply=wait.until(ExpectedConditions.elementToBeClickable(replyLoc));
        reply.click();
        System.out.println(" 点击回复按钮成功");
        // 输入回复私信内容
        By contentLoc=By.cssSelector("#index_message_reply_receive_content");
        WebElement content=wait.until(ExpectedConditions.elementToBeClickable(contentLoc));
        new Actions(driver)
                .click(content) // 激活光标
                .keyDown(Keys.CONTROL).sendKeys("a").keyUp(Keys.CONTROL) // 全选清空
                .sendKeys("测试站内信功能") // 输入内容
                .perform();
        Thread.sleep(1000);
        System.out.println(" 输入回复私信内容成功");
        // 点击"发送"按钮
        By sendLoc=By.cssSelector("#btn_index_send_message_reply");
        WebElement send=wait.until(ExpectedConditions.elementToBeClickable(sendLoc));
        send.click();
        System.out.println(" 点击发送按钮成功");
        // 通过站内信页面的"成功"提示来判断回复功能是否正常
        // 显性等待 jq-toast-single jq-has-icon jq-icon-success
        By toastLocator = By.cssSelector("div.jq-toast-single.jq-icon-success:not([style*='display: none'])");
        wait.until(ExpectedConditions.visibilityOfElementLocated(toastLocator));
        // 获取成功提示
        String Success = driver.findElement(By.cssSelector(".jq-toast-heading")).getText();
        System.out.println(Success);
        // 截图
        getScreenshot(getClass().getName());
        if (Success.equals("成功")) {
            System.out.println("回复成功");
        } else {
            System.out.println("回复失败");
        }
        driver.quit();
    }

}

6.8 我的帖子 MyPostPageTest

`HomePageMyArtical()``:测试我的帖子页面是否正常显示

java 复制代码
package com.hr.forum.integration.ui.tests;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 29113
 * Date: 2026-02-09
 * Time: 20:02
 */

import com.hr.forum.integration.ui.common.Utils;
import org.openqa.selenium.By;

import java.io.IOException;
import java.time.Duration;

/**
 * 我的帖子测试类
 */
public class MyPostPageTest extends Utils {
    public static String url="http://47.98.40.153:8080/index.html";
    public MyPostPageTest() {
        super(url);
    }
    // 测试我的帖子页面是否正常显示
    public void HomePageMyArtical() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofMillis(5000));
        // 先获得个人获取昵称
        String nickNameBe = driver.findElement(By.cssSelector("#index_nav_nickname")).getAttribute("innerHTML");
        // 点击菜单栏个人头像
        driver.findElement(By.cssSelector("#index_nav_avatar")).click();
        // 再点击我的帖子
        driver.findElement(By.cssSelector("#index_user_profile")).click();
        // 通过查看我的帖子中昵称是否是本人来判断跳转功能是否正常
        // 用Lambda 显性等待,等待我的帖子页昵称元素渲染完成
        String nicKNameAf = wait.until(driver -> {
            String innerHTML = driver.findElement(By.cssSelector("#profile_nickname")).getText().trim();
            return !innerHTML.isEmpty() ? innerHTML : null;
        });
        // 截图
        getScreenshot(getClass().getName());
        // 打印昵称
        System.out.println(nicKNameAf);
        // 验证跳转功能是否正常
        assert nickNameBe.equals(nicKNameAf);
        Utils.quit();
    }

}

6.9 个人中心 PersonalCenterPageTest

testPersonalCenter():修改个人信息 头像
testEditPersonalCenter():修改个人信息、昵称、邮箱地址、电话号码
testEditPassword():测试修改密码

java 复制代码
package com.hr.forum.integration.ui.tests;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 29113
 * Date: 2026-02-09
 * Time: 20:25
 */

import com.hr.forum.integration.ui.common.Utils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;

import java.io.IOException;

/**
 * 个人中心页面测试类
 */
public class PersonalCenterPageTest extends Utils {
    public static String url="http://47.98.40.153:8080/index.html";

    public PersonalCenterPageTest() {
        super(url);
    }
    // 修改个人信息 头像
    public void testPersonalCenter() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 点击菜单栏个人头像
        By avatarLoc = By.cssSelector("#index_nav_avatar");
        WebElement avatar =  wait.until(ExpectedConditions.elementToBeClickable(avatarLoc));
        avatar.click();
        // 点击个人中心
        By myCenterLoc = By.cssSelector("#index_user_settings");
        WebElement myCenter =  wait.until(ExpectedConditions.elementToBeClickable(myCenterLoc));
        myCenter.click();
        Thread.sleep(500);
        // 点击"修改头像"按钮
        By changeAvatarLoc = By.cssSelector("#bit-forum-content > div.page-body > div > div > div > div.col.d-flex.flex-column > div > div.row.align-items-center > div:nth-child(2) > a");
        WebElement changeAvatar =  wait.until(ExpectedConditions.elementToBeClickable(changeAvatarLoc));
        changeAvatar.click();
        // 定位上传框
        WebElement avatarUploadInput = driver.findElement(By.id("settings_input_chooiceAvatar"));
        // 传你自己电脑里的文件路径
        avatarUploadInput.sendKeys("D:\\code\\tests\\【哲风壁纸】动漫角色-卡通-妮妮.png");
        // 等待上传成功提示
        // 显性等待 jq-toast-single jq-has-icon jq-icon-success
        By toastLocator = By.cssSelector("div.jq-toast-single.jq-icon-success:not([style*='display: none'])");
        wait.until(ExpectedConditions.visibilityOfElementLocated(toastLocator));
        // 获取成功提示
        String Success = driver.findElement(By.cssSelector(".jq-toast-heading")).getText();
        System.out.println(Success);
        // 截图
        getScreenshot(getClass().getName());
        if (Success.equals("成功")) {
            System.out.println("上传成功");
        } else {
            System.out.println("上传失败");
        }
        // 退出
        driver.quit();
    }
    // 修改个人信息 昵称  邮箱地址 电话号码
    public void testEditPersonalCenter() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 点击菜单栏个人头像
        By avatarLoc = By.cssSelector("#index_nav_avatar");
        WebElement avatar =  wait.until(ExpectedConditions.elementToBeClickable(avatarLoc));
        avatar.click();
        // 点击个人中心
        By myCenterLoc = By.cssSelector("#index_user_settings");
        WebElement myCenter =  wait.until(ExpectedConditions.elementToBeClickable(myCenterLoc));
        myCenter.click();
        Thread.sleep(500);
        // 修改昵称
        By nicknameLoc = By.cssSelector("#setting_input_nickname");
        WebElement nickname =  wait.until(ExpectedConditions.elementToBeClickable(nicknameLoc));
        nickname.clear();
        nickname.click();
        nickname.sendKeys("昵称自动化测试");
        // 点击修改昵称按钮
        By changeNicknameLoc = By.cssSelector("#setting_submit_nickname");
        WebElement changeNickname =  wait.until(ExpectedConditions.elementToBeClickable(changeNicknameLoc));
        changeNickname.click();
        // 截屏
        getScreenshot(getClass().getName());
        // 修改邮箱地址
        By emailLoc = By.cssSelector("#setting_input_email");
        WebElement email =  wait.until(ExpectedConditions.elementToBeClickable(emailLoc));
        email.clear();
        email.click();
        email.sendKeys("<EMAIL>");
        // 点击修改邮箱地址按钮
        By changeEmailLoc = By.cssSelector("#setting_submit_email");
        WebElement changeEmail =  wait.until(ExpectedConditions.elementToBeClickable(changeEmailLoc));
        changeEmail.click();
        // 截屏
        getScreenshot(getClass().getName());
        // 修改电话号码按钮
        By phoneLoc = By.cssSelector("#setting_input_phoneNum");
        WebElement phone =  wait.until(ExpectedConditions.elementToBeClickable(phoneLoc));
        phone.clear();
        phone.click();
        // 点击修改电话号码按钮
        By changePhoneLoc = By.cssSelector("#setting_submit_phoneNum");
        WebElement changePhone =  wait.until(ExpectedConditions.elementToBeClickable(changePhoneLoc));
        changePhone.click();
        // 截屏
        getScreenshot(getClass().getName());
        // 修改个人简介
        By remarkLoc = By.cssSelector("#settings_textarea_remark");
        WebElement remark =  wait.until(ExpectedConditions.elementToBeClickable(remarkLoc));
        remark.clear();
        remark.click();
        remark.sendKeys("个人简介自动化测试");
        // 点击修改个人简介按钮
        By changeRemarkLoc = By.cssSelector("#settings_submit_remark");
        WebElement changeRemark =  wait.until(ExpectedConditions.elementToBeClickable(changeRemarkLoc));
        changeRemark.click();
        // 截屏
        getScreenshot(getClass().getName());
    }
    // 测试修改密码
    public void testEditPassword() throws InterruptedException, IOException {
        // 登录
        Utils.login();
        // 点击菜单栏个人头像
        By avatarLoc = By.cssSelector("#index_nav_avatar");
        WebElement avatar =  wait.until(ExpectedConditions.elementToBeClickable(avatarLoc));
        avatar.click();
        // 点击个人中心
        By myCenterLoc = By.cssSelector("#index_user_settings");
        WebElement myCenter =  wait.until(ExpectedConditions.elementToBeClickable(myCenterLoc));
        myCenter.click();
        Thread.sleep(500);
        // 修改密码
        By oldPasswordLoc = By.cssSelector("#settings_input_oldPassword");
        WebElement oldPassword =  wait.until(ExpectedConditions.elementToBeClickable(oldPasswordLoc));
        oldPassword.sendKeys("123456");
        By newPasswordLoc = By.cssSelector("#settings_input_newPassword");
        WebElement newPassword =  wait.until(ExpectedConditions.elementToBeClickable(newPasswordLoc));
        newPassword.sendKeys("1234567");
        By confirmPasswordLoc = By.cssSelector("#settings_input_passwordRepeat");
        WebElement confirmPassword =  wait.until(ExpectedConditions.elementToBeClickable(confirmPasswordLoc));
        confirmPassword.sendKeys("1234567");
        // 点击"提交修改"按键
        By changePasswordLoc = By.cssSelector("#settings_submit_password");
        WebElement changePassword =  wait.until(ExpectedConditions.elementToBeClickable(changePasswordLoc));
        changePassword.click();
        // 截屏
        getScreenshot(getClass().getName());
        // 通过获取个人页面的"成功"提示来判断发帖功能是否正常
        // 显性等待 jq-toast-single jq-has-icon jq-icon-success
        By toastLocator = By.cssSelector("div.jq-toast-single.jq-icon-success:not([style*='display: none'])");
        wait.until(ExpectedConditions.visibilityOfElementLocated(toastLocator));
        // 获取成功提示
        String Success = driver.findElement(By.cssSelector(".jq-toast-heading")).getText();
        // 截图
        getScreenshot(getClass().getName());
        if (Success.equals("成功")) {
            System.out.println("修改成功");
        } else {
            System.out.println("修改失败");
        }
        // 退出
        Utils.quit();
    }
}

七 、接口测试

7.1 注册接口

7.2 登录接口

7.3 板块相关的接口

7.4 文章相关的接口

7.5 用户相关的接口

7.6 站内信相关的接口

八、性能测试

8.1 聚会报告

8.2 TPS可视化

本次测试覆盖20 + 个业务接口(含帖子、用户、站内信、板块等核心模块),测试目标为验证接口在并发请求下的事务处理能力,图表展示了各接口在测试过程中每秒事务数(TPS)的变化趋势

1. 关键指标分析

  1. 事务处理能力(TPS)
    整体 TPS 范围:多数接口的 TPS 稳定在 1-6 之间,核心接口(如 "修改用户信息""获取板块列表")的 TPS 峰值可达 7-8;
    波动情况:在00:01:26左右出现 TPS 峰值(部分接口达 8),随后快速回落,说明该时段系统处理请求的压力短暂升高,但仍处于稳定范围;
    最低 TPS:所有接口的 TPS 未低于 1,无 "事务中断" 或 "无响应" 的情况。
  2. 接口稳定性
    成功率:图表中所有接口均标注 "(success)",说明测试过程中接口成功率为 100%,无请求失败、超时或错误响应;
    波动合理性:TPS 波动符合业务实际(如 "创建帖子""发送站内信" 等接口的请求量随测试流程动态变化),无异常骤降 / 骤升。

2. 风险与优化建议

  1. 潜在风险
    部分接口(如 "获取用户信息""板块接口事务")在00:01:26时段的 TPS 峰值接近 8,若并发量进一步提升,可能出现响应延迟;
    未体现响应时间、错误率等核心性能指标,需结合聚合报告补充验证。
  2. 优化建议
    对 TPS 峰值较高的接口(如 "创建帖子""发送站内信")进行单独压测,验证其极限承载能力;
    补充响应时间、CPU / 内存使用率等系统资源指标,完善性能评估维度。

3. 测试结论

本次测试中,所有接口的事务处理能力符合预期,成功率 100%,系统在当前并发量下表现稳定,可支持业务正常运行;建议后续补充高并发场景测试,进一步验证系统极限性能。

8.3 响应时间可视化

本次测试针对20 + 个核心业务接口(覆盖帖子、用户、站内信、板块等模块),图表展示了各接口在测试过程中的响应时间(单位:ms) 变化趋势,用于评估系统在并发请求下的响应效率

1. 关键指标分析

  1. 响应时间分布
    基准响应时间:多数接口的响应时间稳定在 1000ms 以内(符合 "用户无明显等待感" 的体验标准);
    峰值响应时间:部分接口(如 "获取用户信息""板块接口事务")出现多次响应时间峰值,最高达 10000ms 以上(10 秒),主要集中在00:00:28、00:01:06等时段;
    波动幅度:响应时间波动剧烈,部分接口在短时间内从 "1000ms 内" 骤升至 "10000ms+",说明系统响应效率受请求压力影响较大。
  2. 接口性能差异
    高效接口:"修改用户信息""发送站内信" 等接口的响应时间多数时段维持在 2000ms 以内,性能表现稳定;
    低效接口:"获取用户信息""板块接口事务" 等接口频繁出现高响应时间峰值,是本次测试的性能瓶颈点。

2. 风险与问题定位

  1. 核心风险
    部分接口响应时间超过 5000ms(5 秒),已超出用户可接受的等待阈值(通常≤3 秒),会导致业务操作体验差;
    响应时间波动剧烈,说明系统资源(如数据库连接、线程池)可能存在 "不足" 或 "分配不合理" 的情况,无法稳定承载请求压力。
  2. 潜在瓶颈点推测
    数据库层面:"获取用户信息""板块接口事务" 等接口涉及数据查询 / 关联操作,可能存在 SQL 未优化、索引缺失的情况;
    系统资源层面:测试过程中服务器 CPU / 内存使用率可能过高,或线程池、连接池配置不足,导致请求排队等待。
    四、优化建议
    优先优化高响应接口:对 "获取用户信息""板块接口事务" 等瓶颈接口,进行 SQL 优化、添加索引,或引入缓存(如 Redis)减少数据库查询;
    调整系统资源配置:扩容服务器资源,或优化线程池、数据库连接池的参数(如增加最大连接数);
    补充资源监控:测试时增加服务器 CPU、内存、磁盘 IO 等监控指标,精准定位资源瓶颈;
    分场景压测:针对高响应接口单独进行压测,验证优化效果。

3. 测试结论

本次测试中,多数接口的基准响应时间符合预期,但部分核心接口存在响应时间过高、波动剧烈的问题,系统在当前配置下的响应效率无法满足高并发业务场景的体验要求;需针对瓶颈接口进行优化后,重新验证性能。

8.4 性能测试报告



1. 整体表现

成功率:本次测试共执行 3636 次请求,错误率为 0.00%,所有请求均成功,系统稳定性表现优异。

APDEX 指数:整体 Apdex 为 0.815,达到 "良好" 水平(>0.7),用户体验整体可接受。

吞吐量:系统整体吞吐量为 39.09 Transactions/s,在当前负载下具备较好的处理能力。

2. 核心指标分析

  1. 响应时间
    平均响应时间为 423.82 ms,中位数为 265.00 ms,大部分请求响应迅速。
    99% 分位响应时间为 3854.68 ms,存在明显长尾请求,表明在高负载下部分请求出现延迟。
    部分接口(如 "修改用户头像接口"、"用户相关的接口"、"板块接口事务")的最大响应时间超过 10 秒,是主要性能瓶颈。
  2. APDEX 表现
    大部分核心业务接口(如 "发送站内信接口"、"回复站内信接口")Apdex 指数在 0.95 以上,用户体验优秀。
    "用户相关的接口"(Apdex 0.018)和 "修改用户头像接口"(Apdex 0.038)Apdex 指数极低,表明这两个模块的用户体验极差,需要重点优化。

3. 风险与建议

  • 高风险点:"用户相关的接口" 和 "修改用户头像接口" 响应时间过长,严重影响用户体验,是首要优化对象。
  • 优化建议:
  1. 针对响应时间过长的接口,排查数据库查询、文件 I/O 等耗时操作,进行 SQL 优化或引入缓存。
  2. 对文件上传接口(如 "修改用户头像"),检查文件存储和处理逻辑,优化上传和处理流程。
  3. 补充服务器资源监控,分析 CPU、内存、数据库连接池等指标,定位资源瓶颈。

4. 最终结论

在本次测试负载下,系统整体稳定性良好,无请求失败,大部分核心接口性能表现优秀。但部分关键业务接口(用户相关、头像修改)存在严重的响应时间问题,导致用户体验不佳,需要优先进行针对性优化,以满足高并发场景下的性能要求。

九、兼容性测试

场景一:以edge浏览器进行测试






场景二:以chrome浏览器进行测试






结果:两个浏览器均可正常兼容网站

十、安全测试

场景一:未登录状况下,是否能访问除登陆外的其它板块

结果:无法访问切将强制到登录页面

场景二: 用户密码是否在数据库加密

结果:用户的密码在数据库中已加密

十一、网络测试

场景一:有网络情况下访问网页

结果:所有网页均可正常访问

场景二:无网络情况下访问网页

结果:所有网页均不可正常访问

十二、存在bug/缺陷

1. 登录接口明文传输

通过Fiddler抓捕工具,可以查看出登录接口账号密码明文传输

2. 并发修改数据,数据不同步

在并发注册/登录回复评论、点赞,私信等操作时数据无法同步

3. 部分接口响应时间过长

用户相关接口、头像上传、板块事务接口最大响应超 10 秒,APEXD 极低,影响用户体验,需优化 SQL、索引、连接池、文件上传逻辑

十三、测试总结

13.1 测试概况

本次对 ideaPool 论坛系统 开展了全流程、多维度质量验证,测试范围覆盖项目全部核心业务模块,包括:用户注册 / 登录、文章管理、回复互动、板块分类、站内信、个人中心 等。本次测试共设计并执行测试用例 163 个,覆盖功能测试、自动化测试、接口测试、性能测试、兼容性测试、安全测试、网络异常测试 等多种测试类型,执行测试覆盖率97% ,全面验证系统在正常、异常、并发、多环境下的表现

13.2 测试范围与内容

功能测试

  • 验证用户注册、登录、发帖、回帖、点赞、搜索、站内信、个人信息修改、头像上传、密码修改、板块 切换等所有业务流程的正确性,覆盖正常场景、异常参数、边界值、必填项校验

UI 自动化测试

  • 基于 Selenium 实现核心页面自动化用例,包括注册、登录、首页、发帖、帖子详情、站内信、我的帖子、个人中心 等模块,实现流程自动化回归与校验

接口测试

  • 注册、登录、板块、文章、用户、站内信 等核心接口进行请求 / 响应验证,确保接口返回格式统一、业务逻辑正确、异常处理规范

性能测试

  • 使用 JMeter 对系统进行并发压测,评估接口 TPS、响应时间、吞吐量、错误率、APDEX 等指标,识别性能瓶颈

兼容性测试

  • Chrome、Edge 两大主流浏览器上验证页面展示、交互逻辑、功能流程一致性

安全测试

  • 验证未登录访问拦截 、用户密码数据库加密存储权限控制等安全机制

网络异常测试

  • 验证在有网 / 断网场景下系统的表现与容错能力

13.3 测试结果

功能质量

  • 系统整体功能流程完整,核心**业务链路(注册→登录→发帖→回帖→点赞→站内信→个人中心)**均可正常执行。
  • 前端校验、后端参数校验、权限拦截逻辑均生效,异常场景可正确提示

自动化测试

  • 完成主要业务场景自动化用例开发与执行,用例可稳定运行,支持后续版本快速回归
  • 实现公共驱动、登录封装、自动截图 等基础能力,自动化框架结构清晰、可维护

接口质量

  • 所有核心接口均可正常请求与响应,返回格式统一、状态码规范
  • 后端采用统一返回体 + 全局异常处理,接口可读性与稳定性良好

性能表现

  • 压测请求 3636 次,错误率 0.00%,系统稳定性良好。
  • 整体吞吐量 39.09 TPS ,APDEX 指数 0.815 ,整体体验良好
  • 平均响应时间 423.82 ms ,大部分接口响应正常;用户相关接口、头像上传接口存在长尾延迟,为主要性能瓶颈

兼容性

  • Chrome、Edge 浏览器下页面渲染、按钮交互、功能流程均正常,兼容性表现良好

安全性

  • 未登录访问受保护页面会自动跳转登录页,权限控制有效
  • 数据库密码已做 MD5 加密存储,敏感信息不泄露

网络容错

  • 有网络环境下功能完全可用 ;无网络时页面合理提示不可访问,符合预期

13.4 发现问题与缺陷

登录接口账号密码明文传输

  • 通过抓包可查看用户名、密码明文,存在信息泄露风险 ,需要增加前端加密(如 RSA/AES)或启用 HTTPS

并发场景下数据不一致

  • 高并发注册、发帖、回复、点赞、私信等操作 时,存在数据不同步问题,需加锁、事务控制或乐观锁 / 分布式锁优化

部分接口响应时间过长

  • 用户相关接口、头像上传、板块事务接口最大响应超 10 秒APEXD 极低,影响用户体验,需优化 SQL、索引、连接池、文件上传逻辑

13.5 整体结论

ideaPool 论坛系统已具备完整可用的业务功能与稳定的运行能力:

  • 功能流程完整、权限控制有效、兼容性良好
  • 接口规范、错误率为 0,满足基础线上运行要求;
  • 性能整体达标,但部分高频接口存在明显瓶颈
  • 存在明文传输、并发数据不同步两类高风险安全 / 稳定性问题。
  • 综合评估:系统可正常上线使用,但存在待优化问题:接口加密、并发安全、性能瓶颈三项关键优化 ,优化后可显著提升安全性、并发稳定性与用户体验。
相关推荐
MarkHD1 小时前
Selenium环境搭建完全指南:WebDriver版本匹配与生产级配置实践
selenium·测试工具
像少年啦飞驰点、1 小时前
零基础入门 RabbitMQ:从消息队列是什么到 Spring Boot 实战收发消息
java·spring boot·微服务·消息队列·rabbitmq·异步编程
MarkHD1 小时前
Selenium环境搭建完全指南:WebDriver版本匹配与生产级配置实践(Day 21-23)
selenium·测试工具
v***57001 小时前
SpringBoot项目集成ONLYOFFICE
java·spring boot·后端
阿萨德528号1 小时前
Spring Boot实战:从零构建企业级用户中心系统(八)- 总结与最佳实践
java·spring boot·后端
爱上妖精的尾巴2 小时前
8-8 WPS JS宏 正则表达式 字符组与任选
java·服务器·前端
一嘴一个橘子2 小时前
mybatis-plus 基于 service 接口的 crud
java
Java小卷2 小时前
KIE Drools 10.x 规则引擎快速入门
java·后端