基于 Selenium + POM 模式的聊天室系统 UI 自动化测试框架搭建与实践

文章目录

    • 一、项目背景
      • [1.1 被测系统简介](#1.1 被测系统简介)
      • [1.2 技术选型](#1.2 技术选型)
    • 二、框架设计
      • [2.1 目录结构](#2.1 目录结构)
      • [2.2 Page Object Model 设计模式](#2.2 Page Object Model 设计模式)
    • 三、核心代码实现
      • [3.1 工具类 Utils.java](#3.1 工具类 Utils.java)
      • [3.2 测试入口 RunTests.java](#3.2 测试入口 RunTests.java)
      • [3.3 注册页面测试 RegisterPage.java](#3.3 注册页面测试 RegisterPage.java)
      • [3.4 聊天页面测试 ChatPage.java](#3.4 聊天页面测试 ChatPage.java)
    • 四、测试用例设计
      • [4.1 测试用例统计](#4.1 测试用例统计)
      • [4.2 边界测试设计](#4.2 边界测试设计)
    • 五、关键技术点
      • [5.1 显式等待处理异步加载](#5.1 显式等待处理异步加载)
      • [5.2 动态元素处理](#5.2 动态元素处理)
      • [5.3 弹窗处理](#5.3 弹窗处理)
      • [5.4 自动截图](#5.4 自动截图)
    • 六、测试执行与报告
      • [6.1 运行测试](#6.1 运行测试)
      • [6.2 测试结果输出](#6.2 测试结果输出)
    • 七、总结
      • [7.1 项目亮点](#7.1 项目亮点)
      • [7.2 后续优化方向](#7.2 后续优化方向)

📌 本文亮点:采用 Page Object Model 设计模式,实现聊天室系统的 UI 自动化测试,涵盖注册、登录、聊天等核心功能,共计 22 个测试用例。

一、项目背景

在 Web 应用开发中,UI 自动化测试是保障产品质量的重要手段。本文以一个实时聊天室系统为例,介绍如何使用 Selenium WebDriver 构建完整的 UI 自动化测试框架。

1.1 被测系统简介

该项目是一个基于 Spring Boot + WebSocket 的实时多用户网页聊天室,核心功能包括:

  • 用户注册与登录
  • 私聊/群聊
  • 消息发送(文本、表情、图片、文件)
  • 消息撤回与引用回复
  • 好友管理与群组创建
  • 主题切换与背景设置

1.2 技术选型

技术 版本 说明
Selenium WebDriver 4.31.0 Web 自动化测试框架
WebDriverManager 5.x 浏览器驱动自动管理
Java 17 编程语言
JUnit - 测试框架(本项目采用 main 方法运行)

二、框架设计

2.1 目录结构

复制代码
src/test/java/com/example/chatroom/test/
├── commons/
│   └── Utils.java          # 公共工具类
└── tests/
    ├── RunTests.java       # 测试入口
    ├── RegisterPage.java   # 注册页测试
    ├── LoginPage.java      # 登录页测试
    └── ChatPage.java       # 聊天页测试

2.2 Page Object Model 设计模式

POM 模式的核心思想是将页面元素定位与测试逻辑分离,提高代码的可维护性和复用性。

三、核心代码实现

3.1 工具类 Utils.java

工具类封装了浏览器驱动管理、截图、弹窗处理等公共方法:

java 复制代码
public class Utils {

    public static WebDriver driver = null;
    public WebDriverWait wait = null;
    public static String baseUrl = "http://localhost:8080";

    public Utils(String url) {
        driver = createDriver();
        driver.get(url);
        wait = new WebDriverWait(driver, Duration.ofSeconds(10));
    }

    public static WebDriver createDriver() {
        if (null == driver) {
            WebDriverManager.chromedriver()
                .driverVersion("146.0.7680.178")
                .setup();
            
            ChromeOptions options = new ChromeOptions();
            options.addArguments("--remote-allow-origins=*");
            options.addArguments("--disable-notifications");
            
            driver = new ChromeDriver(options);
            driver.manage().timeouts()
                .implicitlyWait(Duration.ofSeconds(5));
            driver.manage().window().maximize();
        }
        return driver;
    }

    public void ScreenShot(String str) throws IOException {
        SimpleDateFormat sim1 = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat sim2 = new SimpleDateFormat("HHmmssSS");
        
        String fileName = "./src/test/java/images/" 
            + sim1.format(System.currentTimeMillis()) 
            + "/" + str + "-" 
            + sim2.format(System.currentTimeMillis()) + ".png";
        
        File srcFile = ((TakesScreenshot) driver)
            .getScreenshotAs(OutputType.FILE);
        FileUtils.copyFile(srcFile, new File(fileName));
    }

    public void alertAccept() {
        wait.until(ExpectedConditions.alertIsPresent());
        Alert alert = driver.switchTo().alert();
        alert.accept();
    }
}

3.2 测试入口 RunTests.java

RunTests 只负责调度和统计,不包含具体测试逻辑:

java 复制代码
public class RunTests {

    private static int passCount = 0;
    private static int failCount = 0;

    public static void main(String[] args) throws IOException, InterruptedException {
        System.out.println("========== 聊天室系统UI自动化测试开始 ==========");
        System.out.println("测试时间: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
            .format(System.currentTimeMillis()));
        System.out.println();

        runRegisterTests();
        runLoginTests();
        runChatTests();
        runLogoutTests();

        printSummary();
        Utils.quitDriver();
    }

    private static void runRegisterTests() {
        System.out.println("========== 1. 注册页面测试 ==========");
        try {
            RegisterPage registerPage = new RegisterPage();
            passCount += registerPage.runAllTests();
            System.out.println("注册页面测试通过 ✓\n");
        } catch (Exception e) {
            System.out.println("注册页面测试失败 ✗: " + e.getMessage() + "\n");
            failCount++;
        }
    }

    private static void printSummary() {
        System.out.println("========== 测试结果汇总 ==========");
        System.out.println("测试通过: " + passCount + " 个");
        System.out.println("测试失败: " + failCount + " 个");
        System.out.println("测试通过率: " + (passCount * 100 / (passCount + failCount)) + "%");
        System.out.println("========== 聊天室系统UI自动化测试完成 ==========");
    }
}

3.3 注册页面测试 RegisterPage.java

注册页面包含密码强度校验的边界测试:

java 复制代码
public class RegisterPage extends Utils {

    public static String url = baseUrl + "/register.html";
    private static SimpleDateFormat sim = new SimpleDateFormat("yyyyMMddHHmmssSS");

    public RegisterPage() {
        super(url);
    }

    public int runAllTests() throws IOException {
        int count = 0;

        System.out.println(">>> 测试注册页面元素...");
        checkPageRight();
        count++;

        System.out.println(">>> 测试注册失败场景...");
        registerFail();
        count++;

        System.out.println(">>> 测试注册成功场景...");
        registerSuc();
        count++;

        return count;
    }

    public void checkPageRight() {
        driver.findElement(By.cssSelector("#username"));
        driver.findElement(By.cssSelector("#password"));
        driver.findElement(By.cssSelector("#confirm-password"));
        driver.findElement(By.cssSelector("#strength-bar"));
        driver.findElement(By.cssSelector("#password-tips"));
        System.out.println("注册页面元素检查通过");
    }

    public void registerFail() throws IOException {
        System.out.println("  - 测试空用户名...");
        registerWithAlert("", "Test123456", "Test123456");
        
        System.out.println("  - 测试密码长度不足...");
        registerWithAlert("testuser", "Test1", "Test1");
        
        System.out.println("  - 测试密码缺少大写字母...");
        registerWithAlert("testuser", "test123456", "test123456");
        
        System.out.println("  - 测试密码缺少小写字母...");
        registerWithAlert("testuser", "TEST123456", "TEST123456");
        
        System.out.println("  - 测试密码缺少数字...");
        registerWithAlert("testuser", "TestPassword", "TestPassword");
        
        System.out.println("  - 测试两次密码不一致...");
        registerWithAlert("testuser", "Test123456", "Test654321");
    }

    public String registerSuc() throws IOException {
        String timestamp = sim.format(System.currentTimeMillis());
        String username = "testuser" + timestamp;
        String password = "Test123456";

        driver.findElement(By.cssSelector("#username")).clear();
        driver.findElement(By.cssSelector("#password")).clear();
        driver.findElement(By.cssSelector("#confirm-password")).clear();

        driver.findElement(By.cssSelector("#username")).sendKeys(username);
        driver.findElement(By.cssSelector("#password")).sendKeys(password);
        driver.findElement(By.cssSelector("#confirm-password")).sendKeys(password);

        ScreenShot(Thread.currentThread().getStackTrace()[1].getMethodName());

        driver.findElement(By.cssSelector("#submit")).click();

        wait.until(ExpectedConditions.alertIsPresent());
        Alert alert = driver.switchTo().alert();
        System.out.println("注册弹窗: " + alert.getText());
        alert.accept();

        wait.until(ExpectedConditions.urlContains("login.html"));
        System.out.println("注册成功,用户名: " + username);

        return username;
    }
}

3.4 聊天页面测试 ChatPage.java

聊天页面测试涵盖消息发送、表情、主题切换等功能:

java 复制代码
public class ChatPage extends Utils {

    public static String url = baseUrl + "/client.html";

    public ChatPage() {
        super(url);
    }

    public int runAllTests() throws IOException {
        int count = 0;

        System.out.println(">>> 测试聊天页面元素...");
        checkPageRight();
        count++;

        System.out.println(">>> 测试发送文本消息...");
        sendTextMessage("自动化测试消息 - " + System.currentTimeMillis());
        count++;

        System.out.println(">>> 测试表情选择器...");
        openEmojiPicker();
        count++;

        System.out.println(">>> 测试夜间模式切换...");
        toggleTheme();
        count++;

        System.out.println(">>> 测试消息撤回...");
        recallLastMessage();
        count++;

        // ... 更多测试用例

        return count;
    }

    public void sendTextMessage(String message) throws IOException {
        driver.findElement(By.cssSelector("#message-input")).clear();
        driver.findElement(By.cssSelector("#message-input")).sendKeys(message);
        driver.findElement(By.cssSelector("#send-btn")).click();
        
        sleep(500);
        ScreenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
        System.out.println("发送消息: " + message);
    }

    public void recallLastMessage() throws IOException {
        WebElement lastMessage = driver.findElement(
            By.cssSelector(".message-item:last-child .recall-btn"));
        lastMessage.click();
        
        wait.until(ExpectedConditions.alertIsPresent());
        Alert alert = driver.switchTo().alert();
        alert.accept();
        
        ScreenShot(Thread.currentThread().getStackTrace()[1].getMethodName());
        System.out.println("消息已撤回");
    }
}

四、测试用例设计

4.1 测试用例统计

模块 用例数 覆盖功能
注册模块 10 页面元素、密码强度校验、注册流程
登录模块 3 页面元素、登录成功/失败
聊天模块 15 消息收发、表情、主题、好友、群聊
退出模块 1 退出登录
总计 22 -

4.2 边界测试设计

针对密码强度校验功能,设计了以下边界测试:

复制代码
密码规则:6-12位,必须包含大小写字母和数字

测试场景:
├── 长度边界
│   ├── 5位(不足)→ 弱
│   ├── 6位(最小)→ 校验通过
│   └── 13位(超出)→ 弱
├── 字符类型
│   ├── 只有小写 → 弱
│   ├── 只有大写 → 弱
│   ├── 只有数字 → 弱
│   ├── 小写+大写 → 中
│   ├── 小写+数字 → 中
│   └── 大写+小写+数字 → 强
└── 特殊场景
    ├── 空密码 → 提示
    └── 两次不一致 → 提示

五、关键技术点

5.1 显式等待处理异步加载

聊天室系统大量使用 WebSocket 实时通信,需要使用显式等待处理异步加载:

java 复制代码
// 等待弹窗出现
wait.until(ExpectedConditions.alertIsPresent());

// 等待元素可见
wait.until(ExpectedConditions.visibilityOfElementLocated(
    By.cssSelector("#message-list")));

// 等待 URL 变化
wait.until(ExpectedConditions.urlContains("login.html"));

5.2 动态元素处理

聊天消息列表中的消息是动态生成的,采用以下策略:

java 复制代码
// 使用 last-child 定位最后一条消息
WebElement lastMessage = driver.findElement(
    By.cssSelector(".message-item:last-child"));

// 使用列表遍历所有消息
List<WebElement> messages = driver.findElements(
    By.cssSelector(".message-item"));

5.3 弹窗处理

系统使用 Alert 弹窗进行提示,需要统一处理:

java 复制代码
public void alertAccept() {
    wait.until(ExpectedConditions.alertIsPresent());
    Alert alert = driver.switchTo().alert();
    alert.accept();
}

public String getAlertText() {
    wait.until(ExpectedConditions.alertIsPresent());
    Alert alert = driver.switchTo().alert();
    return alert.getText();
}

5.4 自动截图

每个测试步骤自动截图,便于问题定位:

java 复制代码
public void ScreenShot(String str) throws IOException {
    String fileName = "./src/test/java/images/" 
        + new SimpleDateFormat("yyyy-MM-dd").format(System.currentTimeMillis())
        + "/" + str + "-" 
        + new SimpleDateFormat("HHmmssSS").format(System.currentTimeMillis()) 
        + ".png";
    
    File srcFile = ((TakesScreenshot) driver)
        .getScreenshotAs(OutputType.FILE);
    FileUtils.copyFile(srcFile, new File(fileName));
}

六、测试执行与报告

6.1 运行测试

bash 复制代码
# 方式一:IDE 直接运行 RunTests.java 的 main 方法

# 方式二:命令行运行
java com.example.chatroom.test.tests.RunTests

6.2 测试结果输出

复制代码
========== 聊天室系统UI自动化测试开始 ==========
测试时间: 2026-04-06 15:30:00

========== 1. 注册页面测试 ==========
>>> 测试注册页面元素...
注册页面元素检查通过
>>> 测试注册失败场景...
  - 测试空用户名...
  - 测试密码长度不足...
  - 测试密码缺少大写字母...
  - 测试密码缺少小写字母...
  - 测试密码缺少数字...
  - 测试两次密码不一致...
>>> 测试注册成功场景...
注册成功,用户名: testuser20260406153000123
注册页面测试通过 ✓

========== 2. 登录页面测试 ==========
>>> 测试登录页面元素...
登录页面元素检查通过
>>> 测试登录失败场景...
登录失败弹窗: 用户名或密码错误
>>> 测试登录成功场景...
登录成功
登录页面测试通过 ✓

========== 3. 聊天页面测试 ==========
>>> 测试聊天页面元素...
聊天页面元素检查通过
>>> 测试发送文本消息...
发送消息: 自动化测试消息 - 1712392200000
>>> 测试表情选择器...
表情选择器已打开
>>> 测试夜间模式切换...
主题已切换
>>> 测试消息撤回...
消息已撤回
聊天页面测试通过 ✓

========== 4. 退出登录测试 ==========
>>> 测试退出登录...
退出登录成功
退出登录测试通过 ✓

========== 测试结果汇总 ==========
测试通过: 22 个
测试失败: 0 个
测试通过率: 100%
========== 聊天室系统UI自动化测试完成 ==========

七、总结

7.1 项目亮点

  1. POM 设计模式:页面与测试逻辑分离,代码结构清晰
  2. 边界测试覆盖:密码强度校验覆盖 7 种边界场景
  3. 异步处理:使用显式等待处理 WebSocket 异步消息
  4. 自动截图:每个测试步骤自动截图,便于问题定位
  5. 测试报告:控制台输出详细的测试过程和结果汇总

7.2 后续优化方向

方向 说明
测试框架集成 集成 JUnit/TestNG,支持注解和断言
测试报告 集成 Allure/ExtentReports 生成 HTML 报告
数据驱动 使用 Excel/YAML 管理测试数据
并行执行 支持多线程并行执行测试用例
CI/CD 集成 集成到 Jenkins/GitHub Actions

📝 作者 :独断万古他化

📅 更新时间 :2026-04-06

💬 欢迎留言讨论,点赞收藏是对作者最大的支持!

相关推荐
幸福在路上wellbeing2 小时前
Android Compose UI 控件
android·ui
FreeBuf_2 小时前
Nginx-UI 备份恢复漏洞 PoC 公开:攻击者可篡改加密备份并注入恶意配置
运维·nginx·ui
lovingsoft2 小时前
VSCode+Claude Code+Playwright-MCP 配置实操|零踩坑,1分钟打通AI浏览器自动化
人工智能·vscode·自动化
workflower11 小时前
注塑机行业目前自动化现状分析
运维·人工智能·语言模型·自动化·集成测试·软件工程·软件需求
笨笨饿13 小时前
30_泰勒级数
c语言·stm32·嵌入式硬件·线性代数·机器学习·自动化·概率论
测试_AI_一辰15 小时前
AI 如何参与 Playwright 自动化维护:一次自动修复闭环实践
人工智能·算法·ai·自动化·ai编程
Wild_Pointer.16 小时前
高效工具实战指南:Beyond Compare文件比较工具
测试工具
arvin_xiaoting16 小时前
OpenClaw学习总结_III_自动化系统_1:Hooks详解
运维·学习·自动化
Three~stone18 小时前
Wireshark 4.6.4 安装教程
网络·测试工具·wireshark