Commit e89724e5cdcfc12a45bf565909d7f183ad7b9ed5
1 parent
f495f9c8
✨ feat(im): 修复单测报错的问题
Showing
5 changed files
with
154 additions
and
131 deletions
yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImPrivateMessageMapperTest.java
| ... | ... | @@ -5,7 +5,6 @@ import cn.iocoder.yudao.module.im.dal.dataobject.message.ImPrivateMessageDO; |
| 5 | 5 | import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum; |
| 6 | 6 | import jakarta.annotation.Resource; |
| 7 | 7 | import org.junit.jupiter.api.Test; |
| 8 | -import org.springframework.context.annotation.Import; | |
| 9 | 8 | |
| 10 | 9 | import java.time.LocalDateTime; |
| 11 | 10 | import java.util.List; |
| ... | ... | @@ -17,7 +16,6 @@ import static org.junit.jupiter.api.Assertions.*; |
| 17 | 16 | * |
| 18 | 17 | * @author 芋道源码 |
| 19 | 18 | */ |
| 20 | -@Import(ImPrivateMessageMapper.class) | |
| 21 | 19 | public class ImPrivateMessageMapperTest extends BaseDbUnitTest { |
| 22 | 20 | |
| 23 | 21 | @Resource | ... | ... |
yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImplTest.java
| 1 | 1 | package cn.iocoder.yudao.module.im.service.message; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 3 | 4 | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| 5 | +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | |
| 6 | +import cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender; | |
| 4 | 7 | import cn.iocoder.yudao.module.im.controller.admin.message.vo.group.ImGroupMessageSendReqVO; |
| 5 | 8 | import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO; |
| 6 | 9 | import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO; |
| 7 | 10 | import cn.iocoder.yudao.module.im.dal.dataobject.message.ImGroupMessageDO; |
| 8 | 11 | import cn.iocoder.yudao.module.im.dal.mysql.message.ImGroupMessageMapper; |
| 9 | 12 | import cn.iocoder.yudao.module.im.dal.redis.group.GroupReadPositionRedisDAO; |
| 10 | -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 11 | 13 | import cn.iocoder.yudao.module.im.enums.message.ImGroupMessageReceiptStatusEnum; |
| 12 | 14 | import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum; |
| 13 | -import cn.iocoder.yudao.module.im.service.group.ImGroupService; | |
| 14 | 15 | import cn.iocoder.yudao.module.im.service.group.ImGroupMemberService; |
| 16 | +import cn.iocoder.yudao.module.im.service.group.ImGroupService; | |
| 15 | 17 | import cn.iocoder.yudao.module.im.service.sensitiveword.ImSensitiveWordService; |
| 16 | -import cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender; | |
| 17 | 18 | import org.junit.jupiter.api.Test; |
| 18 | -import org.junit.jupiter.api.extension.ExtendWith; | |
| 19 | 19 | import org.mockito.InjectMocks; |
| 20 | 20 | import org.mockito.Mock; |
| 21 | -import org.mockito.junit.jupiter.MockitoExtension; | |
| 22 | 21 | |
| 23 | 22 | import java.time.LocalDateTime; |
| 24 | 23 | import java.util.*; |
| ... | ... | @@ -28,14 +27,12 @@ import static org.junit.jupiter.api.Assertions.*; |
| 28 | 27 | import static org.mockito.ArgumentMatchers.*; |
| 29 | 28 | import static org.mockito.Mockito.*; |
| 30 | 29 | |
| 31 | -// DONE @芋艿:已评估,23 个测试全部通过 | |
| 32 | 30 | /** |
| 33 | 31 | * IM 群聊消息 Service 单元测试 |
| 34 | 32 | * |
| 35 | 33 | * @author 芋道源码 |
| 36 | 34 | */ |
| 37 | -@ExtendWith(MockitoExtension.class) | |
| 38 | -public class ImGroupMessageServiceImplTest { | |
| 35 | +public class ImGroupMessageServiceImplTest extends BaseMockitoUnitTest { | |
| 39 | 36 | |
| 40 | 37 | @InjectMocks |
| 41 | 38 | private ImGroupMessageServiceImpl groupMessageService; | ... | ... |
yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageServiceImplTest.java
| ... | ... | @@ -2,19 +2,18 @@ package cn.iocoder.yudao.module.im.service.message; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.extra.spring.SpringUtil; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| 5 | +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | |
| 6 | +import cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender; | |
| 5 | 7 | import cn.iocoder.yudao.module.im.controller.admin.message.vo.privates.ImPrivateMessageSendReqVO; |
| 6 | 8 | import cn.iocoder.yudao.module.im.dal.dataobject.message.ImPrivateMessageDO; |
| 7 | 9 | import cn.iocoder.yudao.module.im.dal.mysql.message.ImPrivateMessageMapper; |
| 8 | 10 | import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum; |
| 9 | 11 | import cn.iocoder.yudao.module.im.service.friend.ImFriendService; |
| 10 | 12 | import cn.iocoder.yudao.module.im.service.sensitiveword.ImSensitiveWordService; |
| 11 | -import cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender; | |
| 12 | 13 | import org.junit.jupiter.api.Test; |
| 13 | -import org.junit.jupiter.api.extension.ExtendWith; | |
| 14 | 14 | import org.mockito.InjectMocks; |
| 15 | 15 | import org.mockito.Mock; |
| 16 | 16 | import org.mockito.MockedStatic; |
| 17 | -import org.mockito.junit.jupiter.MockitoExtension; | |
| 18 | 17 | |
| 19 | 18 | import java.time.LocalDateTime; |
| 20 | 19 | import java.util.List; |
| ... | ... | @@ -29,8 +28,7 @@ import static org.mockito.Mockito.*; |
| 29 | 28 | * |
| 30 | 29 | * @author 芋道源码 |
| 31 | 30 | */ |
| 32 | -@ExtendWith(MockitoExtension.class) | |
| 33 | -public class ImPrivateMessageServiceImplTest { | |
| 31 | +public class ImPrivateMessageServiceImplTest extends BaseMockitoUnitTest { | |
| 34 | 32 | |
| 35 | 33 | @InjectMocks |
| 36 | 34 | private ImPrivateMessageServiceImpl privateMessageService; |
| ... | ... | @@ -155,22 +153,28 @@ public class ImPrivateMessageServiceImplTest { |
| 155 | 153 | |
| 156 | 154 | @Test |
| 157 | 155 | public void testReadMessages_success() { |
| 158 | - // 准备:mock 未读消息列表 | |
| 159 | - List<ImPrivateMessageDO> unreadMessages = List.of( | |
| 160 | - ImPrivateMessageDO.builder().id(1L).senderId(2L).receiverId(1L) | |
| 161 | - .status(ImMessageStatusEnum.UNREAD.getStatus()).build(), | |
| 162 | - ImPrivateMessageDO.builder().id(2L).senderId(2L).receiverId(1L) | |
| 163 | - .status(ImMessageStatusEnum.UNREAD.getStatus()).build() | |
| 164 | - ); | |
| 165 | - when(imPrivateMessageMapper.selectListBySenderIdAndReceiverIdAndStatus(2L, 1L, | |
| 166 | - ImMessageStatusEnum.UNREAD.getStatus())).thenReturn(unreadMessages); | |
| 156 | + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { | |
| 157 | + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(ImPrivateMessageServiceImpl.class))) | |
| 158 | + .thenReturn(privateMessageService); | |
| 167 | 159 | |
| 168 | - // 调用 | |
| 169 | - privateMessageService.readPrivateMessages(1L, 2L); | |
| 160 | + // 准备:mock 未读消息列表 | |
| 161 | + List<ImPrivateMessageDO> unreadMessages = List.of( | |
| 162 | + ImPrivateMessageDO.builder().id(1L).senderId(2L).receiverId(1L) | |
| 163 | + .status(ImMessageStatusEnum.UNREAD.getStatus()).build(), | |
| 164 | + ImPrivateMessageDO.builder().id(2L).senderId(2L).receiverId(1L) | |
| 165 | + .status(ImMessageStatusEnum.UNREAD.getStatus()).build() | |
| 166 | + ); | |
| 167 | + when(imPrivateMessageMapper.selectListBySenderIdAndReceiverIdAndStatus(2L, 1L, | |
| 168 | + ImMessageStatusEnum.UNREAD.getStatus())).thenReturn(unreadMessages); | |
| 170 | 169 | |
| 171 | - // 断言:更新了消息状态 + 发送了 READ 和 RECEIPT 事件 | |
| 172 | - verify(imPrivateMessageMapper).update(any(ImPrivateMessageDO.class), any()); | |
| 173 | - verify(webSocketMessageSender, times(2)).sendObject(anyInt(), anyLong(), anyString(), any()); | |
| 170 | + // 调用 | |
| 171 | + privateMessageService.readPrivateMessages(1L, 2L); | |
| 172 | + | |
| 173 | + // 断言:更新了消息状态 + 发送了 READ 和 RECEIPT 事件 | |
| 174 | + verify(imPrivateMessageMapper).updateByIdsAndStatus(eq(List.of(1L, 2L)), | |
| 175 | + eq(ImMessageStatusEnum.UNREAD.getStatus()), any(ImPrivateMessageDO.class)); | |
| 176 | + verify(webSocketMessageSender, times(2)).sendObject(anyInt(), anyLong(), anyString(), any()); | |
| 177 | + } | |
| 174 | 178 | } |
| 175 | 179 | |
| 176 | 180 | // ========== 撤回测试 ========== |
| ... | ... | @@ -208,7 +212,8 @@ public class ImPrivateMessageServiceImplTest { |
| 208 | 212 | // 准备 |
| 209 | 213 | ImPrivateMessageDO message = ImPrivateMessageDO.builder() |
| 210 | 214 | .id(10L).senderId(2L).receiverId(1L) |
| 211 | - .status(ImMessageStatusEnum.UNREAD.getStatus()).build(); | |
| 215 | + .status(ImMessageStatusEnum.UNREAD.getStatus()) | |
| 216 | + .sendTime(LocalDateTime.now()).build(); | |
| 212 | 217 | when(imPrivateMessageMapper.selectById(10L)).thenReturn(message); |
| 213 | 218 | |
| 214 | 219 | // 调用并断言 |
| ... | ... | @@ -222,7 +227,8 @@ public class ImPrivateMessageServiceImplTest { |
| 222 | 227 | // 准备 |
| 223 | 228 | ImPrivateMessageDO message = ImPrivateMessageDO.builder() |
| 224 | 229 | .id(10L).senderId(1L).receiverId(2L) |
| 225 | - .status(ImMessageStatusEnum.RECALL.getStatus()).build(); | |
| 230 | + .status(ImMessageStatusEnum.RECALL.getStatus()) | |
| 231 | + .sendTime(LocalDateTime.now()).build(); | |
| 226 | 232 | when(imPrivateMessageMapper.selectById(10L)).thenReturn(message); |
| 227 | 233 | |
| 228 | 234 | // 调用并断言 | ... | ... |
yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImWebSocketEventTest.java
| 1 | 1 | package cn.iocoder.yudao.module.im.service.message; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | |
| 4 | +import cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender; | |
| 3 | 5 | import cn.iocoder.yudao.module.im.controller.admin.message.vo.privates.ImPrivateMessageSendReqVO; |
| 4 | 6 | import cn.iocoder.yudao.module.im.dal.dataobject.message.ImPrivateMessageDO; |
| 5 | 7 | import cn.iocoder.yudao.module.im.dal.mysql.message.ImPrivateMessageMapper; |
| 6 | 8 | import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum; |
| 9 | +import cn.iocoder.yudao.module.im.service.friend.ImFriendService; | |
| 10 | +import cn.iocoder.yudao.module.im.service.sensitiveword.ImSensitiveWordService; | |
| 7 | 11 | import cn.iocoder.yudao.module.im.websocket.ImMessageReadMessage; |
| 8 | 12 | import cn.iocoder.yudao.module.im.websocket.ImMessageReceiptMessage; |
| 9 | -import cn.iocoder.yudao.module.im.websocket.ImMessageRecallMessage; | |
| 10 | 13 | import cn.iocoder.yudao.module.im.websocket.ImPrivateMessageSendMessage; |
| 11 | -import cn.iocoder.yudao.module.im.service.friend.ImFriendService; | |
| 12 | -import cn.iocoder.yudao.module.im.service.sensitiveword.ImSensitiveWordService; | |
| 13 | -import cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender; | |
| 14 | 14 | import org.junit.jupiter.api.Test; |
| 15 | -import org.junit.jupiter.api.extension.ExtendWith; | |
| 16 | 15 | import org.mockito.ArgumentCaptor; |
| 17 | 16 | import org.mockito.InjectMocks; |
| 18 | 17 | import org.mockito.Mock; |
| 19 | -import org.mockito.junit.jupiter.MockitoExtension; | |
| 20 | 18 | |
| 21 | 19 | import java.util.List; |
| 22 | 20 | |
| 23 | -import static org.junit.jupiter.api.Assertions.*; | |
| 21 | +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| 22 | +import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| 24 | 23 | import static org.mockito.ArgumentMatchers.*; |
| 25 | 24 | import static org.mockito.Mockito.*; |
| 26 | 25 | |
| ... | ... | @@ -31,8 +30,7 @@ import static org.mockito.Mockito.*; |
| 31 | 30 | * |
| 32 | 31 | * @author 芋道源码 |
| 33 | 32 | */ |
| 34 | -@ExtendWith(MockitoExtension.class) | |
| 35 | -public class ImWebSocketEventTest { | |
| 33 | +public class ImWebSocketEventTest extends BaseMockitoUnitTest { | |
| 36 | 34 | |
| 37 | 35 | @InjectMocks |
| 38 | 36 | private ImPrivateMessageServiceImpl privateMessageService; |
| ... | ... | @@ -48,92 +46,104 @@ public class ImWebSocketEventTest { |
| 48 | 46 | |
| 49 | 47 | @Test |
| 50 | 48 | public void testSendMessage_websocketEvent() { |
| 51 | - // 准备 | |
| 52 | - ImPrivateMessageSendReqVO reqVO = new ImPrivateMessageSendReqVO(); | |
| 53 | - reqVO.setClientMessageId("evt-uuid"); | |
| 54 | - reqVO.setReceiverId(2L); | |
| 55 | - reqVO.setType(0); | |
| 56 | - reqVO.setContent("{\"content\":\"事件测试\"}"); | |
| 57 | - | |
| 58 | - when(imPrivateMessageMapper.selectBySenderIdAndClientMessageId(1L, "evt-uuid")).thenReturn(null); | |
| 59 | - // mock insert:通过 doAnswer 模拟 MyBatis-Plus 设置 ID | |
| 60 | - when(imPrivateMessageMapper.insert(any(ImPrivateMessageDO.class))).thenAnswer(invocation -> { | |
| 61 | - ImPrivateMessageDO msg = invocation.getArgument(0); | |
| 62 | - msg.setId(99L); | |
| 63 | - return 1; | |
| 64 | - }); | |
| 65 | - | |
| 66 | - // 调用 | |
| 67 | - privateMessageService.sendPrivateMessage(1L, reqVO); | |
| 68 | - | |
| 69 | - // 捕获 WebSocket 调用 | |
| 70 | - ArgumentCaptor<String> typeCaptor = ArgumentCaptor.forClass(String.class); | |
| 71 | - ArgumentCaptor<Object> contentCaptor = ArgumentCaptor.forClass(Object.class); | |
| 72 | - verify(webSocketMessageSender, times(2)).sendObject(anyInt(), anyLong(), | |
| 73 | - typeCaptor.capture(), contentCaptor.capture()); | |
| 74 | - | |
| 75 | - // 验证消息事件类型 | |
| 76 | - assertEquals(ImPrivateMessageSendMessage.TYPE, typeCaptor.getAllValues().get(0)); | |
| 77 | - assertEquals(ImPrivateMessageSendMessage.TYPE, typeCaptor.getAllValues().get(1)); | |
| 78 | - | |
| 79 | - // 验证 payload 是 DTO 类型 | |
| 80 | - ImPrivateMessageSendMessage payload = (ImPrivateMessageSendMessage) contentCaptor.getAllValues().get(0); | |
| 81 | - assertNotNull(payload.getId(), "id 不应为空"); | |
| 82 | - assertEquals("1", payload.getSenderId()); | |
| 83 | - assertEquals("2", payload.getReceiverId()); | |
| 49 | + try (var springUtilMockedStatic = mockStatic(cn.hutool.extra.spring.SpringUtil.class)) { | |
| 50 | + springUtilMockedStatic.when(() -> cn.hutool.extra.spring.SpringUtil.getBean(eq(ImPrivateMessageServiceImpl.class))) | |
| 51 | + .thenReturn(privateMessageService); | |
| 52 | + | |
| 53 | + // 准备 | |
| 54 | + ImPrivateMessageSendReqVO reqVO = new ImPrivateMessageSendReqVO(); | |
| 55 | + reqVO.setClientMessageId("evt-uuid"); | |
| 56 | + reqVO.setReceiverId(2L); | |
| 57 | + reqVO.setType(0); | |
| 58 | + reqVO.setContent("{\"content\":\"事件测试\"}"); | |
| 59 | + | |
| 60 | + when(imPrivateMessageMapper.selectBySenderIdAndClientMessageId(1L, "evt-uuid")).thenReturn(null); | |
| 61 | + when(imPrivateMessageMapper.insert(any(ImPrivateMessageDO.class))).thenAnswer(invocation -> { | |
| 62 | + ImPrivateMessageDO msg = invocation.getArgument(0); | |
| 63 | + msg.setId(99L); | |
| 64 | + return 1; | |
| 65 | + }); | |
| 66 | + | |
| 67 | + // 调用 | |
| 68 | + privateMessageService.sendPrivateMessage(1L, reqVO); | |
| 69 | + | |
| 70 | + // 捕获 WebSocket 调用 | |
| 71 | + ArgumentCaptor<String> typeCaptor = ArgumentCaptor.forClass(String.class); | |
| 72 | + ArgumentCaptor<Object> contentCaptor = ArgumentCaptor.forClass(Object.class); | |
| 73 | + verify(webSocketMessageSender, times(2)).sendObject(anyInt(), anyLong(), | |
| 74 | + typeCaptor.capture(), contentCaptor.capture()); | |
| 75 | + | |
| 76 | + // 验证消息事件类型 | |
| 77 | + assertEquals(ImPrivateMessageSendMessage.TYPE, typeCaptor.getAllValues().get(0)); | |
| 78 | + assertEquals(ImPrivateMessageSendMessage.TYPE, typeCaptor.getAllValues().get(1)); | |
| 79 | + | |
| 80 | + // 验证 payload 是 DTO 类型 | |
| 81 | + ImPrivateMessageSendMessage payload = (ImPrivateMessageSendMessage) contentCaptor.getAllValues().get(0); | |
| 82 | + assertNotNull(payload.getId(), "id 不应为空"); | |
| 83 | + assertEquals(1L, payload.getSenderId()); | |
| 84 | + assertEquals(2L, payload.getReceiverId()); | |
| 85 | + } | |
| 84 | 86 | } |
| 85 | 87 | |
| 86 | 88 | @Test |
| 87 | 89 | public void testReadMessage_readAndReceiptEvents() { |
| 88 | - // 准备:mock 未读消息 | |
| 89 | - List<ImPrivateMessageDO> unreadMessages = List.of( | |
| 90 | - ImPrivateMessageDO.builder().id(1L).senderId(2L).receiverId(1L) | |
| 91 | - .status(ImMessageStatusEnum.UNREAD.getStatus()).build() | |
| 92 | - ); | |
| 93 | - when(imPrivateMessageMapper.selectListBySenderIdAndReceiverIdAndStatus(2L, 1L, | |
| 94 | - ImMessageStatusEnum.UNREAD.getStatus())).thenReturn(unreadMessages); | |
| 95 | - | |
| 96 | - // 调用 | |
| 97 | - privateMessageService.readPrivateMessages(1L, 2L); | |
| 98 | - | |
| 99 | - // 捕获 | |
| 100 | - ArgumentCaptor<String> typeCaptor = ArgumentCaptor.forClass(String.class); | |
| 101 | - ArgumentCaptor<Long> userCaptor = ArgumentCaptor.forClass(Long.class); | |
| 102 | - verify(webSocketMessageSender, times(2)).sendObject(anyInt(), userCaptor.capture(), | |
| 103 | - typeCaptor.capture(), any()); | |
| 104 | - | |
| 105 | - // 第一次:发给自己的 READ 事件 | |
| 106 | - assertEquals(ImMessageReadMessage.TYPE, typeCaptor.getAllValues().get(0)); | |
| 107 | - assertEquals(1L, userCaptor.getAllValues().get(0)); | |
| 108 | - // 第二次:发给对方的 RECEIPT 事件 | |
| 109 | - assertEquals(ImMessageReceiptMessage.TYPE, typeCaptor.getAllValues().get(1)); | |
| 110 | - assertEquals(2L, userCaptor.getAllValues().get(1)); | |
| 90 | + try (var springUtilMockedStatic = mockStatic(cn.hutool.extra.spring.SpringUtil.class)) { | |
| 91 | + springUtilMockedStatic.when(() -> cn.hutool.extra.spring.SpringUtil.getBean(eq(ImPrivateMessageServiceImpl.class))) | |
| 92 | + .thenReturn(privateMessageService); | |
| 93 | + | |
| 94 | + // 准备:mock 未读消息 | |
| 95 | + List<ImPrivateMessageDO> unreadMessages = List.of( | |
| 96 | + ImPrivateMessageDO.builder().id(1L).senderId(2L).receiverId(1L) | |
| 97 | + .status(ImMessageStatusEnum.UNREAD.getStatus()).build() | |
| 98 | + ); | |
| 99 | + when(imPrivateMessageMapper.selectListBySenderIdAndReceiverIdAndStatus(2L, 1L, | |
| 100 | + ImMessageStatusEnum.UNREAD.getStatus())).thenReturn(unreadMessages); | |
| 101 | + | |
| 102 | + // 调用 | |
| 103 | + privateMessageService.readPrivateMessages(1L, 2L); | |
| 104 | + | |
| 105 | + // 捕获 | |
| 106 | + ArgumentCaptor<String> typeCaptor = ArgumentCaptor.forClass(String.class); | |
| 107 | + ArgumentCaptor<Long> userCaptor = ArgumentCaptor.forClass(Long.class); | |
| 108 | + verify(webSocketMessageSender, times(2)).sendObject(anyInt(), userCaptor.capture(), | |
| 109 | + typeCaptor.capture(), any()); | |
| 110 | + | |
| 111 | + // 第一次:发给自己的 READ 事件 | |
| 112 | + assertEquals(ImMessageReadMessage.TYPE, typeCaptor.getAllValues().get(0)); | |
| 113 | + assertEquals(1L, userCaptor.getAllValues().get(0)); | |
| 114 | + // 第二次:发给对方的 RECEIPT 事件 | |
| 115 | + assertEquals(ImMessageReceiptMessage.TYPE, typeCaptor.getAllValues().get(1)); | |
| 116 | + assertEquals(2L, userCaptor.getAllValues().get(1)); | |
| 117 | + } | |
| 111 | 118 | } |
| 112 | 119 | |
| 113 | 120 | @Test |
| 114 | - public void testRecallMessage_recallEvent() { | |
| 115 | - // 准备 | |
| 116 | - ImPrivateMessageDO message = ImPrivateMessageDO.builder() | |
| 117 | - .id(10L).senderId(1L).receiverId(2L).status(0).build(); | |
| 118 | - when(imPrivateMessageMapper.selectById(10L)).thenReturn(message); | |
| 119 | - when(imPrivateMessageMapper.updateById(any(ImPrivateMessageDO.class))).thenReturn(1); | |
| 120 | - | |
| 121 | - // 调用 | |
| 122 | - privateMessageService.recallPrivateMessage(1L, 10L); | |
| 123 | - | |
| 124 | - // 捕获 | |
| 125 | - ArgumentCaptor<String> typeCaptor = ArgumentCaptor.forClass(String.class); | |
| 126 | - ArgumentCaptor<Object> contentCaptor = ArgumentCaptor.forClass(Object.class); | |
| 127 | - verify(webSocketMessageSender, times(2)).sendObject(anyInt(), anyLong(), | |
| 128 | - typeCaptor.capture(), contentCaptor.capture()); | |
| 129 | - | |
| 130 | - // 验证 RECALL 事件类型 | |
| 131 | - assertEquals(ImMessageRecallMessage.TYPE, typeCaptor.getAllValues().get(0)); | |
| 132 | - | |
| 133 | - // 验证 payload | |
| 134 | - ImMessageRecallMessage payload = (ImMessageRecallMessage) contentCaptor.getAllValues().get(0); | |
| 135 | - assertEquals("10", payload.getMessageId()); | |
| 136 | - assertEquals("private", payload.getMessageScene()); | |
| 121 | + public void testRecallMessage_tipTextEvent() { | |
| 122 | + try (var springUtilMockedStatic = mockStatic(cn.hutool.extra.spring.SpringUtil.class)) { | |
| 123 | + springUtilMockedStatic.when(() -> cn.hutool.extra.spring.SpringUtil.getBean(eq(ImPrivateMessageServiceImpl.class))) | |
| 124 | + .thenReturn(privateMessageService); | |
| 125 | + | |
| 126 | + // 准备 | |
| 127 | + ImPrivateMessageDO message = ImPrivateMessageDO.builder() | |
| 128 | + .id(10L).senderId(1L).receiverId(2L).status(0) | |
| 129 | + .sendTime(java.time.LocalDateTime.now()).build(); | |
| 130 | + when(imPrivateMessageMapper.selectById(10L)).thenReturn(message); | |
| 131 | + when(imPrivateMessageMapper.updateById(any(ImPrivateMessageDO.class))).thenReturn(1); | |
| 132 | + when(imPrivateMessageMapper.insert(any(ImPrivateMessageDO.class))).thenReturn(1); | |
| 133 | + | |
| 134 | + // 调用 | |
| 135 | + privateMessageService.recallPrivateMessage(1L, 10L); | |
| 136 | + | |
| 137 | + // 捕获 | |
| 138 | + ArgumentCaptor<String> typeCaptor = ArgumentCaptor.forClass(String.class); | |
| 139 | + ArgumentCaptor<Object> contentCaptor = ArgumentCaptor.forClass(Object.class); | |
| 140 | + verify(webSocketMessageSender, times(2)).sendObject(anyInt(), anyLong(), | |
| 141 | + typeCaptor.capture(), contentCaptor.capture()); | |
| 142 | + | |
| 143 | + // 验证:推送的是 TIP_TEXT 消息(通过 sendPrivateMessageEvent) | |
| 144 | + assertEquals(ImPrivateMessageSendMessage.TYPE, typeCaptor.getAllValues().get(0)); | |
| 145 | + assertEquals(ImPrivateMessageSendMessage.TYPE, typeCaptor.getAllValues().get(1)); | |
| 146 | + } | |
| 137 | 147 | } |
| 138 | 148 | |
| 139 | 149 | } | ... | ... |
yudao-module-im/yudao-module-im-biz/src/test/resources/application-unit-test.yaml
| 1 | 1 | spring: |
| 2 | 2 | main: |
| 3 | - lazy-initialization: true | |
| 4 | - banner-mode: off | |
| 3 | + lazy-initialization: true # 开启懒加载,加快速度 | |
| 4 | + banner-mode: off # 单元测试,禁用 Banner | |
| 5 | 5 | |
| 6 | 6 | --- #################### 数据库相关配置 #################### |
| 7 | 7 | |
| 8 | 8 | spring: |
| 9 | + # 数据源配置项 | |
| 9 | 10 | datasource: |
| 10 | 11 | name: ruoyi-vue-pro |
| 11 | - url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; | |
| 12 | + url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写 | |
| 12 | 13 | driver-class-name: org.h2.Driver |
| 13 | 14 | username: sa |
| 14 | 15 | password: |
| 15 | 16 | druid: |
| 16 | - async-init: true | |
| 17 | - initial-size: 1 | |
| 17 | + async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度 | |
| 18 | + initial-size: 1 # 单元测试,配置为 1,提升启动速度 | |
| 18 | 19 | sql: |
| 19 | 20 | init: |
| 20 | 21 | schema-locations: classpath:/sql/create_tables.sql |
| 21 | 22 | encoding: UTF-8 |
| 22 | 23 | |
| 24 | + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 | |
| 23 | 25 | data: |
| 24 | 26 | redis: |
| 25 | - host: 127.0.0.1 | |
| 26 | - port: 16379 | |
| 27 | - database: 0 | |
| 27 | + host: 127.0.0.1 # 地址 | |
| 28 | + port: 16379 # 端口(单元测试,使用 16379 端口) | |
| 29 | + database: 0 # 数据库索引 | |
| 28 | 30 | |
| 29 | -mybatis-plus: | |
| 30 | - lazy-initialization: true | |
| 31 | - type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject | |
| 31 | +mybatis: | |
| 32 | + lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 | |
| 33 | + | |
| 34 | +--- #################### 定时任务相关配置 #################### | |
| 35 | + | |
| 36 | +--- #################### 配置中心相关配置 #################### | |
| 37 | + | |
| 38 | +--- #################### 服务保障相关配置 #################### | |
| 39 | + | |
| 40 | +# Lock4j 配置项(单元测试,禁用 Lock4j) | |
| 41 | + | |
| 42 | +--- #################### 监控相关配置 #################### | |
| 32 | 43 | |
| 33 | 44 | --- #################### 芋道相关配置 #################### |
| 34 | 45 | |
| 46 | +# 芋道配置项,设置当前项目所有自定义的配置 | |
| 35 | 47 | yudao: |
| 36 | 48 | info: |
| 37 | - base-package: cn.iocoder.yudao | |
| 49 | + base-package: cn.iocoder.yudao.module | ... | ... |