目录
[2.1 登录界面的测试用例](#2.1 登录界面的测试用例)
[2.2 注册页面的测试用例](#2.2 注册页面的测试用例)
[2.3 主界面的测试用例](#2.3 主界面的测试用例)
[2.3.1 列表切换模块的测试用例](#2.3.1 列表切换模块的测试用例)
[2.3.2 消息发送模块](#2.3.2 消息发送模块)
[2.3.3 历史消息模块](#2.3.3 历史消息模块)
[3.1 登录界面](#3.1 登录界面)
[3.2 注册界面](#3.2 注册界面)
[3.3 主界面](#3.3 主界面)
[3.3.1 会话列表与好友列表](#3.3.1 会话列表与好友列表)
[3.3.2 查看历史消息](#3.3.2 查看历史消息)
[3.3.3 发送新消息](#3.3.3 发送新消息)
[四. 兼容性测试 与 界面测试](#四. 兼容性测试 与 界面测试)
[五. 自动化测试](#五. 自动化测试)
[5.0 前期准备](#5.0 前期准备)
[5.0.1 导入依赖包---pom.xml](#5.0.1 导入依赖包---pom.xml)
[5.0.2 断言的开启设置](#5.0.2 断言的开启设置)
[5.1 创建 Utils 工具类,放置常用代码和方法](#5.1 创建 Utils 工具类,放置常用代码和方法)
[5.2 注册界面测试](#5.2 注册界面测试)
[5.3 登录界面测试](#5.3 登录界面测试)
[5.4 主界面测试](#5.4 主界面测试)
[5.5 列表切换功能的测试](#5.5 列表切换功能的测试)
[5.6 历史会话功能的测试](#5.6 历史会话功能的测试)
[六. 源代码分享](#六. 源代码分享)
一、项目简介
本项目是基于Spring boot + WebSocket框架实现的双人网页即时聊天系统。采用前后端分离的架构,前端使用HTML和CSS设计页面,发送Ajax请求实现前后端交互。为用户提供完整的在线即时通讯服务。
核心功能包括:用户注册与登录功能、好友管理、单聊创建会话、实时接收、发送消息与历史消息查看。系统通过WebSocket实现消息的实时推送,确保用户间沟通高效同步;同时支持会话复用与好友关系维护,保证数据一致性与良好的用户体验。
二、测试用例设计
2.1 登录界面的测试用例

2.2 注册页面的测试用例

2.3 主界面的测试用例
2.3.1 列表切换模块的测试用例

2.3.2 消息发送模块

2.3.3 历史消息模块

三、手动进行系统功能测试
3.1 登录界面
①正确的用户名、密码、点击"登录"
预期结果:登录成功并自动跳转到主界面


②空的用户名、任意密码、点击"登录"
预期结果:提示"当前用户输入的用户名或密码为空!"


③正确的用户名、错误的密码、点击"登录"
预期结果:提示登录失败!

④点击"→ 去注册 →"按钮
预期结果:跳转到注册页面


3.2 注册界面
① 正常输入登录名、密码、点击注册
预期:注册成功,跳转到登录页面



② 登录名为空、密码随便、点击注册
预期:注册失败,用户名不能为空

③ 正常输入登录名、密码未空、点击注册
预期:注册失败,密码不能为空

④ 输入已存在的用户名,密码随便、点击注册
预期:注册失败,该用户名已存在

3.3 主界面
① 刚进入主页面时,显示的是会话列表,会话列表图标呈现高亮状态,好友列表呈灰暗状态

3.3.1 会话列表与好友列表
①先点击好友列表
预期:显示好友列表,好友列表标签高亮,会话列表标签变暗


②再点击会话列表
预期:会话列表标签变亮,好友列表标签变暗,显示会话列表


3.3.2 查看历史消息
①在会话列表点开一个会话项
预期:当前会话项呈高亮状态,右侧消息区会展示当前会话的历史消息,右侧上方显示会话名称(对话人名称)


②在好友列表点击一个好友项
预期:跳转到好友对应的会话,并打开会话的历史消息


3.3.3 发送新消息
① 好友未上线:当前用户是zhangsan,向好友wangwu发送消息,输入框输入"喂喂喂?",点击发送
预期:在zhangsan的消息区直接显示最新消息。wangwu上线后才看到新消息


wangwu刚上线,发现与zhangsan会话的最新消息的预览是:喂喂喂?

② 好友在线上:当前用户是wangwu,向好友zhangsan发送消息,输入框输入"我在线上",点击发送
预期:双方都能直接看到最新消息的发送
此时wangwu处于在线状态,双方都能看到新消息:


四. 兼容性测试 与 界面测试
①在Chrome浏览器下进入友聊驿站
预期结果:无异常且一切布局显示功能等与其一致






②在Edge浏览器下进入友聊驿站
预期结果:无异常且一切布局显示功能等与其一致






五. 自动化测试
5.0 前期准备
5.0.1 导入依赖包---pom.xml
XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>autoTest</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Selenium Java 最新稳定版 -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.18.1</version>
</dependency>
<!-- WebDriverManager 最新稳定版 -->
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>5.6.3</version>
</dependency>
<!-- 屏幕截图包 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
</project>
5.0.2 断言的开启设置

没 add VM options之前:


输入 "-ea -Dfile.encoding=UTF-8",点击 apply ------> 点击OK

5.1 创建 Utils 工具类,放置常用代码和方法
java
package common;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.apache.commons.io.FileUtils;
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;
//自动化测试的通用工具
public class Utils {
//web驱动器: 全局仅一个
public static WebDriver driver;
//显式等待
public WebDriverWait wait;
//主界面的url(需要登录后才能进入)
//构造方法:注册一次web驱动、根据url打开对应的网页、给对应网页的类new一个显示等待
public Utils(String url){
// 单例模式
driver = createDriver();
// 打开url对应的网页
driver.get(url);
// 给该网页的类分配一个显示等待计时器
wait = new WebDriverWait(driver, Duration.ofSeconds(3));
}
private WebDriver createDriver() {
if (driver == null){
//下载并注册驱动
WebDriverManager.chromedriver().setup();
//添加驱动的配置:可访问所有页面
ChromeOptions options = new ChromeOptions();
options.addArguments("--remote-allow-origins=*");
//创建驱动对象
driver = new ChromeDriver(options);
//为驱动添加全局的隐式等待:3s
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
}
return driver;
}
//截图工具方法
public void screenShot(String funcName, WebDriver driver) throws IOException {
//年月日
SimpleDateFormat sim1 = new SimpleDateFormat("yyyy-MM-dd");
//时分秒毫秒
SimpleDateFormat sim2 = new SimpleDateFormat("HHmmssSS");
String dirTime = sim1.format(System.currentTimeMillis());
String fileTime = sim2.format(System.currentTimeMillis());
//文件路径: ./src/test/java/images/2026-1-22/func-时分秒毫秒.jpg
String fileName = "./src/test/java/images/" + dirTime + "/" + funcName + "-" + fileTime + ".jpg";
File srcFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(srcFile, new File(fileName));
}
}
5.2 注册界面测试
java
package tests;
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;
public class RegisterPage extends Utils {
public static String url = "http://127.0.0.1:8080/register.html";
public RegisterPage() {
super(url);
}
/**
* 检查注册页面的加载完毕
*/
public void checkRegisterPage(){
String text = driver.findElement(By.cssSelector("body > div.login-container > div > h3")).getText();
WebElement username = driver.findElement(By.cssSelector("#username"));
WebElement password = driver.findElement(By.cssSelector("#password"));
WebElement button = driver.findElement(By.cssSelector("#register-btn"));
assert text.equals("注册");
assert username != null;
assert password != null;
assert button != null;
}
/**
* 成功注册 ------------ 用户名和密码都不为空,用户名未被使用
*/
public void registerSuc() throws IOException {
WebElement username = driver.findElement(By.cssSelector("#username"));
WebElement password = driver.findElement(By.cssSelector("#password"));
WebElement button = driver.findElement(By.cssSelector("#register-btn"));
username.clear();
password.clear();
username.sendKeys("abc");
password.sendKeys("abc");
button.click();
// 处理弹窗
Alert alert = wait.until(ExpectedConditions.alertIsPresent());
alert.accept();
// 校验是否注册成功------------跳转到登录页面
String text = driver.findElement(By.cssSelector("body > div.login-container > div > h3")).getText();
assert text.equals("登录");
// 屏幕截图
screenShot(Thread.currentThread().getStackTrace()[1].getMethodName(), driver);
}
/**
* 注册失败
* 1. 仅登录名为空
* 2. 仅密码为空
* 3. 输入已存在的用户名
*/
// 1. 仅登录名为空
public void registerFail1() throws IOException {
WebElement username = driver.findElement(By.cssSelector("#username"));
WebElement password = driver.findElement(By.cssSelector("#password"));
WebElement registerBtn = driver.findElement(By.cssSelector("#register-btn"));
username.clear();
password.sendKeys("asfsdfsd");
registerBtn.click();
// 处理弹窗
Alert alert = wait.until(ExpectedConditions.alertIsPresent());
alert.accept();
// 屏幕截图
screenShot(Thread.currentThread().getStackTrace()[1].getMethodName(), driver);
}
// 2. 仅密码为空
public void registerFail2() throws IOException {
WebElement username = driver.findElement(By.cssSelector("#username"));
WebElement password = driver.findElement(By.cssSelector("#password"));
WebElement registerBtn = driver.findElement(By.cssSelector("#register-btn"));
username.clear();
username.sendKeys("dsdfgsasdwad");
password.clear();
registerBtn.click();
// 处理弹窗
Alert alert = wait.until(ExpectedConditions.alertIsPresent());
alert.accept();
// 屏幕截图
screenShot(Thread.currentThread().getStackTrace()[1].getMethodName(), driver);
}
// 3. 输入已存在的用户名
public void registerFail3() throws IOException {
WebElement username = driver.findElement(By.cssSelector("#username"));
WebElement password = driver.findElement(By.cssSelector("#password"));
WebElement registerBtn = driver.findElement(By.cssSelector("#register-btn"));
username.clear();
username.sendKeys("zhangsan");
password.clear();
password.sendKeys("asfsdfsd");
registerBtn.click();
// 处理弹窗
Alert alert = wait.until(ExpectedConditions.alertIsPresent());
alert.accept();
// 屏幕截图
screenShot(Thread.currentThread().getStackTrace()[1].getMethodName(), driver);
}
}
5.3 登录界面测试
java
package tests;
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;
public class LoginPage extends Utils {
public static String url = "http://127.0.0.1:8080/login.html";
public LoginPage() {
super(url);
}
/**
* 检查登录页面是否加载完毕
*/
public void checkLoginPage() throws IOException {
// 用户名输入框
driver.findElement(By.cssSelector("#username"));
// 密码输入框
driver.findElement(By.cssSelector("#password"));
// "登录"按钮
driver.findElement(By.cssSelector("#login-btn"));
// "注册"按钮
driver.findElement(By.cssSelector("#register-btn"));
// 屏幕截图
screenShot(Thread.currentThread().getStackTrace()[1].getMethodName(), driver);
}
/**
* 跳转到注册页面
*/
public void jumpToRegisterPage() throws IOException {
// 跳转到注册页面
driver.findElement(By.xpath("//*[@id=\"register-btn\"]")).click();
// 验证是否跳转成功(输入框标题为"注册")
String text = driver.findElement(By.cssSelector("body > div.login-container > div > h3")).getText();
// 布尔表达式为false时才触发断言
assert text.equals("注册");
// 截图
screenShot(Thread.currentThread().getStackTrace()[1].getMethodName(), driver);
}
/**
* 成功登录------------正确的用户名和密码
*/
public void loginSuc() throws IOException, InterruptedException {
// 1.输入用户名和密码
WebElement username = driver.findElement(By.cssSelector("#username"));
WebElement password = driver.findElement(By.cssSelector("#password"));
// a) 清空输入框
username.clear();
password.clear();
// b) 用户名:zhangsna; 密码:123
username.sendKeys("zhangsan");
password.sendKeys("123");
// c) 点击登录
driver.findElement(By.cssSelector("#login-btn")).click();
// d) 弹窗确认
Thread.sleep(1000);
Alert alert = driver.switchTo().alert();
alert.accept();
// 2.验证登录是否成功
// 主页面有会话列表的标签
driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-session"));
//屏幕截图
screenShot(Thread.currentThread().getStackTrace()[1].getMethodName(), driver);
}
/**
* 登录失败:
* ------------ 1.用户名和密码都空
* ------------ 2.仅用户名空
* ------------ 3.仅密码空
* ------------ 4.用户名存在,密码错误
*/
// 用户名和密码都空
public void loginFail1() throws InterruptedException, IOException {
// 用户名:空
driver.findElement(By.cssSelector("#username")).clear();
// 密码:空
driver.findElement(By.cssSelector("#password")).clear();
// 点击登录
driver.findElement(By.cssSelector("#login-btn")).click();
// 弹窗确认(ExpectedConditions预期的条件)
Alert alert = wait.until(ExpectedConditions.alertIsPresent());
alert.accept();
//弹窗没有处理没法执行其他操作,所以截图需要在弹窗处理后。否则会报 UnhandledAlertException(未处理的弹窗)
//屏幕截图
screenShot(Thread.currentThread().getStackTrace()[1].getMethodName(), driver);
}
// 仅用户名空
public void loginFail2() throws IOException {
// 用户名:空, 密码:123
driver.findElement(By.cssSelector("#username")).clear();
WebElement password = driver.findElement(By.cssSelector("#password"));
password.clear();
password.sendKeys("123");
// 点击登录
driver.findElement(By.cssSelector("#login-btn")).click();
// 弹窗确认(ExpectedConditions预期的条件)
Alert alert = wait.until(ExpectedConditions.alertIsPresent());
alert.accept();
//屏幕截图
screenShot(Thread.currentThread().getStackTrace()[1].getMethodName(), driver);
}
// 仅密码空
public void loginFail3() throws IOException{
// 用户名:zhangsan, 密码:空
WebElement username = driver.findElement(By.cssSelector("#username"));
username.clear();
username.sendKeys("zhangsan");
driver.findElement(By.cssSelector("#password")).clear();
// 点击登录
driver.findElement(By.cssSelector("#login-btn")).click();
// 弹窗确认(ExpectedConditions预期的条件)
Alert alert = wait.until(ExpectedConditions.alertIsPresent());
alert.accept();
//屏幕截图
screenShot(Thread.currentThread().getStackTrace()[1].getMethodName(), driver);
}
// 用户名存在,密码错误
public void loginFail4() throws IOException {
// 用户名:zhangsan, 密码:321
WebElement username = driver.findElement(By.cssSelector("#username"));
WebElement password = driver.findElement(By.cssSelector("#password"));
username.clear();
password.clear();
username.sendKeys("zhangsan");
password.sendKeys("321");
// 点击登录
driver.findElement(By.cssSelector("#login-btn")).click();
//弹窗确认
Alert alert = wait.until(ExpectedConditions.alertIsPresent());
alert.accept();
//屏幕截图
screenShot(Thread.currentThread().getStackTrace()[1].getMethodName(), driver);
}
}
5.4 主界面测试
节选部分代码【皆选自HomePage类】
java
public class HomePage extends Utils {
private static final Logger log = LoggerFactory.getLogger(HomePage.class);
private static String url = "http://127.0.0.1:8080/client.html";
public WebDriver friendDriver = null;
public HomePage() {
super(url);
createFriendDriver();
}
private void createFriendDriver(){
if (this.friendDriver != null){
return;
}
// 添加驱动的配置:可访问所有页面
ChromeOptions options = new ChromeOptions();
options.addArguments("--remote-allow-origins=*");
// 创建新的浏览器
this.friendDriver = new ChromeDriver(options);
friendDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
}
/**
* 检查主页面是否加载成功: 当前用户是zhangsan
*/
public void checkHomePage(){
// 检查当前登录的用户名、会话列表标签、好友列表标签
String username = driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.user")).getText();
WebElement sessionListLabel = driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-session"));
WebElement friendListLabel = driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-friend"));
assert username.equals("zhangsan");
assert sessionListLabel != null;
assert friendListLabel != null;
}
public void register(String username, String password, WebDriver driver) throws InterruptedException {
// 让浏览器打开注册页面
String registerUrl = RegisterPage.url;
driver.get(registerUrl);
// 输入名字、密码、点击注册
WebElement usernameInput = driver.findElement(By.cssSelector("#username"));
WebElement passwordInput = driver.findElement(By.cssSelector("#password"));
WebElement button = driver.findElement(By.cssSelector("#register-btn"));
usernameInput.clear();
passwordInput.clear();
usernameInput.sendKeys(username);
passwordInput.sendKeys(password);
button.click();
// 处理弹窗
Thread.sleep(1000);
Alert alert = driver.switchTo().alert();
alert.accept();
// 校验是否注册成功------------跳转到登录页面
String text = driver.findElement(By.cssSelector("body > div.login-container > div > h3")).getText();
assert text.equals("登录");
System.out.println(username + "注册成功!");
}
public void login(String username, String password, WebDriver driver) throws InterruptedException {
// 让浏览器打开登录页面
String loginUrl = LoginPage.url;
driver.get(loginUrl);
// 输入名字、密码、点击登录
WebElement usernameInput = driver.findElement(By.cssSelector("#username"));
WebElement passwordInput = driver.findElement(By.cssSelector("#password"));
WebElement button = driver.findElement(By.cssSelector("#login-btn"));
usernameInput.clear();
passwordInput.clear();
usernameInput.sendKeys(username);
passwordInput.sendKeys(password);
button.click();
// 处理弹窗
// 注意:wait是driver浏览器的显示等待,在friendDriver中不能使用
Thread.sleep(1000);
Alert alert = driver.switchTo().alert();
alert.accept();
//验证登录是否成功------------主页面有会话列表的标签
driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-session"));
System.out.println(username + "登录成功!");
}
private void sendFriendRequest(String friendName, WebDriver driver) throws InterruptedException {
// 搜索好友
WebElement searchBox = driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.search > input[type=text]"));
searchBox.sendKeys(friendName);
// 点击搜索
WebElement searchBtn = driver.findElement(By.cssSelector("#searchBtn"));
searchBtn.click();
// 点击"添加好友"
WebElement sendAddBtn = driver.findElement(By.cssSelector("body > div.client-container > div > div.right > div.message-show > div > button"));
sendAddBtn.click();
// 处理弹窗
Thread.sleep(1000);
Alert alert = driver.switchTo().alert();
alert.accept();
}
}
5.5 列表切换功能的测试
java
/**
* 测试好友列表与会话列表标签的点击状态
*/
public void clickListLabels() throws IOException {
// 截图查看初始状态
screenShot(Thread.currentThread().getStackTrace()[1].getMethodName(), driver);
// 点击好友列表后, 查看标签状态
WebElement friendListLabel = driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-friend"));
friendListLabel.click();
screenShot(Thread.currentThread().getStackTrace()[1].getMethodName(), driver);
// 点击会话列表后, 查看标签状态
WebElement sessionListLabel = driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-session"));
sessionListLabel.click();
screenShot(Thread.currentThread().getStackTrace()[1].getMethodName(), driver);
}
5.6 历史会话功能的测试
java
/**
* 点开一个会话项
* 预期:右侧显示 会话标题 和 历史会话
*/
public void clickSessionItem() throws InterruptedException, IOException {
// 点击好友会话名称为"lisi"的会话项
// driver.findElement(By.xpath("//*[@id=\"session-list\"]/li[3]/h3/..")).click();
driver.findElement(By.xpath("//h3[text()=\"lisi\"]/parent::li")).click();
// Thread.sleep(2000);
// 查看右侧标题和消息区
String title = driver.findElement(By.cssSelector("body > div.client-container > div > div.right > div.title")).getText();
WebElement message = driver.findElement(By.cssSelector("body > div.client-container > div > div.right > div.message-show > div:nth-child(9) > div > p"));
// 当message存在时再拉取(不需要可见时)
// WebElement message = wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector("body > div.client-container > div > div.right > div.message-show > div:nth-child(9) > div > p")));
assert title.equals("lisi");
assert message != null;
// 截图
screenShot(Thread.currentThread().getStackTrace()[1].getMethodName(), driver);
}
/**
* 在好友列表点击一个好友项
* 预期:跳转到好友对应的会话,并打开会话的历史消息。该会话的会话项放在首位
*/
public void clickFriendItem() throws IOException {
// 0. 点击好友列表
WebElement friendListLabel = driver.findElement(By.cssSelector("body > div.client-container > div > div.left > div.tab > div.tab-friend"));
friendListLabel.click();
// 1. 点击好友"lisi"
WebElement friendLi = driver.findElement(By.xpath("//ul[@id=\"friend-list\"]/li[./h4[text()=\"lisi\"]]"));
friendLi.click();
// 2. 左侧自动跳转到会话列表, 查看会话名为"lisi"的会话项是否在首位
String sessionName = driver.findElement(By.cssSelector("#session-list > li:nth-child(1) > h3")).getText();
assert sessionName.equals("lisi");
// 3. 查看右侧标题和消息区
String title = driver.findElement(By.cssSelector("body > div.client-container > div > div.right > div.title")).getText();
WebElement message = driver.findElement(By.cssSelector("body > div.client-container > div > div.right > div.message-show > div:nth-child(9) > div > p"));
assert title.equals("lisi");
assert message != null;
// 截图
screenShot(Thread.currentThread().getStackTrace()[1].getMethodName(), driver);
}
六. 源代码分享
自动化测试代码的github链接:https://github.com/QiLuNuo928/autoTest_java_chatroom
