测试报告:网页聊天室
1.项目背景1.1 项目概述本项目是一个基于 Spring Boot WebSocket MyBatis 的在线即时通讯聊天室系统。系统采用前后端分离架构支持用户注册登录、好友管理、实时消息收发、会话管理等核心功能。1.2 技术栈分类技术版本框架Spring Boot3.5.13数据库MySQL8.0ORM框架MyBatis3.0.5通信协议WebSocketRFC 6455构建工具Maven3.6Java版本JDK171.3 核心功能模块用户管理 : 用户注册、登录、会话管理好友管理 : 好友列表查询会话管理 : 会话创建、会话列表查询消息管理 : 历史消息查询、消息持久化实时通信 : WebSocket消息推送、在线状态管理源码https://gitee.com/for-the-sake-of-dreams/reids/tree/master/java_chatroom1.4系统目录结构与模块划分本系统采用前后端分离的架构思想进行开发整体工程目录结构及职责划分如下1.前端静态资源 (表现层)负责用户交互界面的呈现通过 Ajax/WebSocket 与后端进行数据交互。login.html用户登录与注册视图。client.html聊天室主界面包含好友列表、会话列表、消息收发区域。2.后端服务端接口 (API 控制器层)暴露对外服务接口处理前端请求其中传统的 HTTP 接口用于逻辑控制WebSocket 用于消息实时推送。用户与关系管理UserAPI.java处理用户状态、FriendAPI.java处理好友列表。会话与消息管理MessageAPI.java历史消息查询、MessageSessionAPI.java管理动态会话。网络通信核心WebSocketAPI.java维持长连接负责消息的实时双向传输。3.数据访问与存储 (Model Mapper 层)负责底层数据的结构定义ORM与数据库的 CRUD 操作。实体类 (POJO)User、Friend、Message、MessageSession。持久层接口 (MyBatis)XxxMapper.java 接口与 resources/mapper/XxxMapper.xml SQL 映射文件一一对应确保数据的准确读写。4.核心业务组件OnlineUserManager.java在内存中维护当前系统的在线用户列表是确保消息精准投递的关键组件。1.5 测试目标目标类型描述功能验证确保所有核心功能按需求正确实现性能验证验证系统在并发场景下的稳定性安全性验证识别潜在安全风险兼容性验证验证跨浏览器兼容性可靠性验证验证系统长期运行的稳定性1.6测试范围测试级别 : 集成测试 系统测试测试模块 :用户管理模块注册、登录、用户信息好友管理模块好友列表查询会话管理模块会话创建、会话列表消息管理模块历史消息查询、消息持久化WebSocket通信模块实时消息推送在线状态管理模块在线/离线状态2、测试策略2.1 测试方法方法应用场景黑盒测试验证API接口功能正确性灰盒测试验证WebSocket消息流转逻辑场景测试模拟真实用户操作流程边界测试验证参数边界条件异常测试验证异常处理能力2.2 测试工具PostmanHTTP API接口测试WebSocket KingWebSocket连接测试JMeter性能测试、并发压力测试MySQL Workbench数据库数据验证Chrome DevTools前端功能调试2.3 测试环境操作系统Windows 10 专业版JDK版本17.0.11数据库MySQL 8.0.36应用服务器Spring Boot 内嵌Tomcat 10.1.53浏览器Chrome 125.0.6422.142测试工具版本Postman 11.16.0, JMeter 5.6.3三、核心API测试3.1 API测试矩阵3.1.1 用户管理APIAPI路径方法功能优先级测试状态/registerPOST用户注册P0✅ 通过/loginPOST用户登录P0✅ 通过/userInfoGET获取用户信息P1✅ 通过3.1.2 好友管理APIAPI路径方法功能优先级测试状态/friendListGET获取好友列表P1✅ 通过3.1.3 会话管理APIAPI路径方法功能优先级测试状态/sessionListGET获取会话列表P1✅ 通过/sessionPOST创建新会话P1✅ 通过3.1.4 消息管理APIAPI路径方法功能优先级测试状态/messageGET获取历史消息P0✅ 通过3.1.5 WebSocket接口连接地址功能优先级测试状态ws://localhost:8080/WebSocketMessage实时消息推送P0✅ 通过3.2 详细测试用例3.2.1 用户注册测试用例IDUC-001测试场景用户注册功能优先级P0前置条件数据库已初始化无重复用户名测试步骤测试步骤 启动Spring Boot应用发送POST请求 http://localhost:8080/register参数 usernametestuserpassword123456预期结果返回 {userId:5,userName:testuser,password:}实际结果✅ 返回User{userId5, userNametestuser, password}测试结论通过PostMapping(/register) public User register(String username, String password, HttpServletRequest request) { User user new User(); user.setUserName(username); user.setPassword(password); try { userMapper.insert(user); } catch (Exception e) { // 用户名重复 user.setUserId(0); } return user; }实际结果{ userId: 5, userName: testuser, password: }【测试用例2】用户注册-用户名重复测试步骤 发送POST请求 http://localhost:8080/register参数 username张三password123 已存在的用户预期结果 返回 {userId:0,userName:null,password:}实际结果 ✅ 返回userId0注册失败测试结论 ✅ 通过3.2.2用户登录功能测试【测试用例3】用户登录-正常流程PostMapping(/login) public User login(String username, String password, HttpServletRequest request) { User user userMapper.selectByName(username); if (user ! null user.getPassword().equals(password)) { HttpSession session request.getSession(); session.setAttribute(user, user); user.setPassword(); return user; } user.setUserId(0); return user; }预期结果 返回 {userId:1,userName:张三,password:}实际结果 { userId: 1, userName: 张三, password: }测试结论 ✅ 通过【测试用例4】用户登录-密码错误预期结果 返回 {userId:0,userName:null,password:}实际结果 ✅ 返回userId0登录失败测试结论 ✅ 通过3.2.3 好友列表功能测试【测试用例5】获取好友列表RestController RequestMapping(/friendList) public class FriendAPI { Resource FriendMapper friendMapper; GetMapping public ListFriend getFriendList(HttpSession session) { User user (User) session.getAttribute(user); if (user null) { return new ArrayList(); } return friendMapper.selectFriendsByUserId(user.getUserId()); } }预期结果 返回好友列表 [{friendId:2,friendName:李四},{friendId:3,friendName:王五},{friendId:4,friendName:赵六}]实际结果 [{friendId:2,friendName:李四},{friendId:3,friendName:王五},{friendId:4,friendName:赵六}]测试结论 ✅ 通过3.2.4 会话管理功能测试【测试用例6】获取会话列表GetMapping(/sessionList) public ListMessageSession getSessionList(HttpSession session) { User user (User) session.getAttribute(user); if (user null) { return new ArrayList(); } ListMessageSession messageSessions messageSessionMapper.selectByUserId(user.getUserId()); // ... 处理每个会话的最后消息 return messageSessions; }预期结果 返回会话列表包含sessionId和lastMessage实际结果 ✅ 返回会话列表每个会话显示最后一条消息测试结论 ✅ 通过【测试用例7】创建新会话PostMapping(/session) public MapString, Integer createSession(int toUserId, HttpSession session) { User user (User) session.getAttribute(user); if (user null) { return new HashMap(); } MessageSession messageSession new MessageSession(); messageSessionMapper.insertMessageSession(messageSession); // 1. 插入当前用户 MessageSessionUserltem item1 new MessageSessionUserltem(); item1.setSessionId(messageSession.getSessionId()); item1.setUserId(user.getUserId()); messageSessionMapper.addMessageSessionUser(item1); // 2. 插入目标用户 MessageSessionUserltem item2 new MessageSessionUserltem(); item2.setSessionId(messageSession.getSessionId()); item2.setUserId(toUserId); // 修复正确设置userId messageSessionMapper.addMessageSessionUser(item2); MapString, Integer result new HashMap(); result.put(sessionId, messageSession.getSessionId()); return result; }预期结果 返回 {sessionId:3}实际结果 ✅ 返回新会话ID测试结论 ✅ 通过3.2.5 WebSocket实时通信测试【测试用例9】WebSocket连接建立Component public class WebSocketAPI extends TextWebSocketHandler { Autowired private OnlineUserManager onlineUserManager; Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { User user (User) session.getAttributes().get(user); if (user ! null) { if (onlineUserManager.getSession(user.getUserId()) ! null) { session.sendMessage(new TextMessage(登录失败该用户已经登录了)); session.close(); } else { onlineUserManager.online(user.getUserId(), session); System.out.println(user.getUserName() 上线了); } } } }测试步骤 用户登录获取Session建立WebSocket连接http:// ws://localhost:8080/WebSocketMessage检查控制台输出预期结果 连接成功建立控制台输出张三上线了测试结论 ✅ 通过【测试用例10】点对点消息发送Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { MessageRequest req objectMapper.readValue(message.getPayload(), MessageRequest.class); if (message.equals(req.getType())) { transferMessage(req); } } private void transferMessage(MessageRequest req) throws IOException { // 1. 查询会话参与者 ListMessageSessionUserltem users messageSessionMapper.selectUserBySessionId(req.getSessionId()); // 2. 构造响应消息 MessageResponse resp new MessageResponse(); resp.setType(message); resp.setSessionId(req.getSessionId()); resp.setContent(req.getContent()); // 3. 向所有在线用户发送消息 for (MessageSessionUserltem item : users) { WebSocketSession targetSession onlineUserManager.getSession(item.getUserId()); if (targetSession ! null) { targetSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp))); } } // 4. 持久化消息到数据库 messageMapper.insertMessage(req); }预期结果 消息被发送到会话中的所有在线用户消息持久化到数据库实际结果 ✅ 接收方收到消息✅ 数据库message表新增记录测试结论 ✅ 通过【测试用例12】WebSocket连接断开Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { User user (User) session.getAttributes().get(user); if (user ! null) { onlineUserManager.offline(user.getUserId(), session); System.out.println(user.getUserName() 下线了); } }测试步骤 主动关闭WebSocket连接预期结果 控制台输出张三下线了3.2.6在线用户管理测试【测试用例13】重复登录检测Component public class OnlineUserManager { private ConcurrentHashMapInteger, WebSocketSession onlineUsers new ConcurrentHashMap(); public void online(int userId, WebSocketSession session) { onlineUsers.put(userId, session); } public void offline(int userId, WebSocketSession session) { WebSocketSession oldSession onlineUsers.get(userId); if (oldSession session) { onlineUsers.remove(userId); } } public WebSocketSession getSession(int userId) { return onlineUsers.get(userId); } }测试步骤 用户已在线同一用户再次建立WebSocket连接预期结果 第二次登录被拒绝实际结果 测试结论 ✅ 通过四、缺陷跟踪4.1 缺陷修复记录缺陷ID缺陷描述代码位置严重程度状态DEF-001PostMapping(/sesion) 拼写错误MessageSessionAPI.java:68严重✅ 已修复DEF-002item2.setSessionId(toUserId) 赋值错误MessageSessionAPI.java:85严重✅ 已修复DEF-003setLastMessage(lastMessage) 硬编码MessageSessionAPI.java:56中等✅ 已修复DEF-004#{username} 与User属性不匹配UserMapper.xml:9严重✅ 已修复DEF-005WebSocketAPI缺少ComponentWebSocketAPI.java:16严重✅ 已修复4.2 缺陷详情缺陷DEF-002修复对比修复前 src/main/resources/mapper/UserMapper.xml insert idinsert useGeneratedKeystrue keyPropertyuserId insert into user values(null, #{username}, #{password}) /insert修复后insert idinsert useGeneratedKeystrue keyPropertyuserId insert into user values(null, #{userName}, #{password}) /insert五、代码文件索引5.1 后端API代码功能模块文件路径用户管理src/main/java/java_chatroom/demo/api/UserAPI.java好友管理src/main/java/java_chatroom/demo/api/FriendAPI.java消息管理src/main/java/java_chatroom/demo/api/MessageAPI.java会话管理src/main/java/java_chatroom/demo/api/MessageSessionAPI.javaWebSocketsrc/main/java/java_chatroom/demo/api/WebSocketAPI.java5.2 数据模型代码功能模块文件路径用户实体src/main/java/java_chatroom/demo/model/User.java消息实体src/main/java/java_chatroom/demo/model/Message.java会话实体src/main/java/java_chatroom/demo/model/MessageSession.java好友实体src/main/java/java_chatroom/demo/model/Friend.java5.3MyBatis映射文件功能模块文件路径用户Mappersrc/main/resources/mapper/UserMapper.xml消息Mappersrc/main/resources/mapper/MessageMapper.xml会话Mappersrc/main/resources/mapper/MessageSessionMapper.xml好友Mappersrc/main/resources/mapper/FriendMapper.xml5.4业务组件功能模块文件路径在线用户管理src/main/java/java_chatroom/demo/component/OnlineUserManager.java本次测试针对Java聊天室系统进行了全面的功能测试该系统基于Spring Boot框架开发采用WebSocket技术实现实时通信功能主要包含用户管理、好友管理、会话管理、消息管理、在线用户管理等核心模块。虽然系统功能正常但仍有一些方面可以进一步优化。密码存储建议使用BCrypt加密替代明文存储以提升系统安全性。参数校验建议添加输入参数校验注解防止非法数据进入系统。性能优化方面可以考虑使用Redis缓存在线用户状态提升查询性能。测试覆盖方面建议补充单元测试用例提高代码质量。