文章目录
-
- 一、项目背景
-
- [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 项目亮点
- POM 设计模式:页面与测试逻辑分离,代码结构清晰
- 边界测试覆盖:密码强度校验覆盖 7 种边界场景
- 异步处理:使用显式等待处理 WebSocket 异步消息
- 自动截图:每个测试步骤自动截图,便于问题定位
- 测试报告:控制台输出详细的测试过程和结果汇总
7.2 后续优化方向
| 方向 | 说明 |
|---|---|
| 测试框架集成 | 集成 JUnit/TestNG,支持注解和断言 |
| 测试报告 | 集成 Allure/ExtentReports 生成 HTML 报告 |
| 数据驱动 | 使用 Excel/YAML 管理测试数据 |
| 并行执行 | 支持多线程并行执行测试用例 |
| CI/CD 集成 | 集成到 Jenkins/GitHub Actions |
📝 作者 :独断万古他化
📅 更新时间 :2026-04-06
💬 欢迎留言讨论,点赞收藏是对作者最大的支持!