Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spring ApplicationEventPublisher 发布事件的测试 #4

Open
Xuguozong opened this issue Jun 13, 2023 · 0 comments
Open

Spring ApplicationEventPublisher 发布事件的测试 #4

Xuguozong opened this issue Jun 13, 2023 · 0 comments
Labels
post should be a completed post test something about testing (UT,IT,ETE...)

Comments

@Xuguozong
Copy link
Owner

  • 前置条件:

    • Spring 版本约束:≥ 5.3.3
    • 根据实际需要决定是否启动 ApplicationContext 去测试
  • 使用方法:

    • 被测方法

      @LogRecord(content = "'在【' + @customerService.listSeaNames(#root) + ' 公海】里领取线索'",
                  kind = CLUE_MANAGE, opType = "领取线索", operationType = OpRemarkType.QUERY,
                  bizIds = "#arg0", sharding = true, collection = SCRM_CUSTOMER, batchOp = true)
      public ModifyResult pickUp(List<String> ids) {
          User user = userService.currentUser();
          String collName = currentCusColl();
          Update update = Update.update(FOLLOW_USER_ZH, user.getName())
                  .set(FOLLOW_USER_EN, user.getId())
                  .set(FOLLOW_USER_DEPT_EN, user.getDept())
                  .set(STATUS_ZH, STATUS_ZH_DISTRIBUTED)
                  .set(FOLLOW_STAFF_STATUS_ZH, "在职")
                  .set(DISTRIBUTED_TIME_ZH, DateUtil.now())
                  .set(LAST_OPERATOR_ZH, user.getName())
                  .set(UPDATE_TIME_ZH, DateUtil.now())
                  .inc("领取次数", 1);
          Query query = Query.query(Criteria.where("_id").in(ids).and(STATUS_ZH).is(STATUS_ZH_UNDISTRIBUTED));
          List<CustomerPart> parts = customerParts(ids, collName);
          List<ReclaimTaskEvent.ClueInfo> infos = parts.stream()
                  .filter(p -> p.getDistributeStatus().equals(STATUS_ZH_UNDISTRIBUTED))
                  .map(p -> new ReclaimTaskEvent.ClueInfo(p.getId(), p.getSeaName()))
                  .toList();
          // 添加回收任务
          eventPublisher.publishEvent(new ReclaimTaskEvent()
                  .setClueInfos(infos)
                  .setCompany(user.getCompany())
                  .setFollowUserId(user.getId())
                  .setFollowUserName(user.getName())
                  .setDistributeTime(DateUtil.current()));
          // 添加操作日志(通过发布事件的形式)
          parts.stream()
                  .filter(c -> c.getDistributeStatus().equals(STATUS_ZH_UNDISTRIBUTED))
                  .forEach(c -> publishOplog(c.getId(), user, CLUE_MANAGE, "领取线索",
                          String.format("在【%s】里领取线索", c.getSeaName()), collName, RESOURCE, false));
          UpdateResult result = mongoTemplate.updateMulti(query, update, collName);
          return MongoUtil.modifyResult(result, ids);
      }
    • 需要启动 ApplicationContetxt 的集成测试

      • @SpringBootTest + @RecordApplicationEvents 注解在测试类上

      • @Autowired ApplicationEvents applicationEvents 作为类属性

      • 需要断言的内容:一般为某个测试方法中期望发布的 ApplicationEvent 和 Spring 容器记录的 applicationEvents 做比对

      • 示例:

        • 测试方法
        @Test
        @DisplayName("a none_distributed clue should be picked up successfully")
        void test_pickup_a_not_distributed() throws Exception {
            String token = defaultToken();
            DistributeRequest request = new DistributeRequest();
            request.setIds(notDistributedOne);
            MockHttpServletRequestBuilder post = post(pickupUrl)
                    .header("token", token)
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(JSON.toJSONString(request));
            mockMvc.perform(post)
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("data.successCount").value(1))
                    .andExpect(jsonPath("data.failedCount").value(0));
            List<PayloadApplicationEvent> events = this.applicationEvents.stream(PayloadApplicationEvent.class).toList();
            assertThat(events.size()).isEqualTo(2);
            assertThat(events.stream().anyMatch(e -> e.getPayload() instanceof OpLog)).isTrue();
            assertThat(events.stream().anyMatch(e -> e.getPayload() instanceof ReclaimTaskEvent)).isTrue();
        }
        
        @AfterEach
        void clear() {
            // clean up recorded events after each test method executed
            applicationEvents.clear();
        }
        • IT 测试基类
        @AutoConfigureMockMvc
        @RecordApplicationEvents
        @Testcontainers(disabledWithoutDocker = true)
        @SpringBootTest(classes = ScrmApiApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
        public abstract class BaseItTest {
        
            @Autowired
            protected MockMvc mockMvc;
        
            @Autowired
            protected ApplicationEvents applicationEvents;
        
            private static final MongoDBContainer mongoDBContainer;
        
            static {
                mongoDBContainer = new MongoDBContainer("mongo:5").withReuse(true);
                mongoDBContainer.start();
            }
        
            @DynamicPropertySource
            public static void setDatasourceProperties(final DynamicPropertyRegistry registry) {
                registry.add("spring.data.mongodb.scrm.uri", mongoDBContainer::getReplicaSetUrl);
                registry.add("spring.data.mongodb.scrm.database", () -> "test");
            }
        
        }
    • 需要 mock 的单元测试

      • 使用 Mockito.veryfy() 框架,对于只发布一个事件的可以使用 ArgumentCaptor 组件
      • 测试方法
      @Test
      void test_pick_a_none_distributed() {
          User user = mockUser();
          when(userService.currentUser()).thenReturn(user);
          // doNothing().when(eventPublisher).publishEvent(any(ReclaimTaskEvent.class));
          // doNothing().when(eventPublisher).publishEvent(any(OpLog.class));
          try (MockedStatic<ServletUtil> servletUtilMockedStatic = Mockito.mockStatic(ServletUtil.class)) {
              try (MockedStatic<ContextUtil> contextUtilMockedStatic = Mockito.mockStatic(ContextUtil.class)) {
                  contextUtilMockedStatic.when(ContextUtil::getServletRequest).thenReturn(null);
                  contextUtilMockedStatic.when(ContextUtil::currentCusColl).thenReturn(cusColl);
                  servletUtilMockedStatic.when(() -> ServletUtil.getClientIP(null)).thenReturn("0.0.0.0");
      
                  ModifyResult result = customerService.pickUp(List.of(notDistributedOne));
                  assertEquals(1, result.getSuccessCount());
                  assertEquals(0, result.getFailedCount());
                  // 更新后数据校验
                  LinkedHashMap updated = findOne(notDistributedOne);
                  assertEquals("已分配", updated.get("分配状态"));
                  assertEquals(user.getId(), updated.get("followUser"));
                  assertEquals(user.getName(), updated.get("跟进人"));
                  assertEquals(user.getName(), updated.get("最近操作人"));
                  assertEquals(1, updated.get("领取次数"));
                  // 校验发布事件类型及次数
                  verify(eventPublisher, times(1)).publishEvent(any(ReclaimTaskEvent.class));
                  verify(eventPublisher, times(1)).publishEvent(any(OpLog.class));
              }
          }
      }
  • 参考:

@Xuguozong Xuguozong added post should be a completed post test something about testing (UT,IT,ETE...) labels Jun 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
post should be a completed post test something about testing (UT,IT,ETE...)
Projects
Status: No status
Status: No status
Development

No branches or pull requests

1 participant