Commit 7c7748d7e8c67d94fa560cb711234b25f10a54ca

Authored by YunaiV
1 parent 8c408cea

【代码重构】工作流:完善 BpmTaskCandidateInvoker 的单元测试

yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java
@@ -100,9 +100,9 @@ public class BpmTaskCandidateInvoker { @@ -100,9 +100,9 @@ public class BpmTaskCandidateInvoker {
100 return new HashSet<>(); 100 return new HashSet<>();
101 } 101 }
102 102
  103 + // 1.1 计算任务的候选人
103 Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement); 104 Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement);
104 String param = BpmnModelUtils.parseCandidateParam(flowElement); 105 String param = BpmnModelUtils.parseCandidateParam(flowElement);
105 - // 1.1 计算任务的候选人  
106 Set<Long> userIds = getCandidateStrategy(strategy).calculateUsersByTask(execution, param); 106 Set<Long> userIds = getCandidateStrategy(strategy).calculateUsersByTask(execution, param);
107 // 1.2 移除被禁用的用户 107 // 1.2 移除被禁用的用户
108 removeDisableUsers(userIds); 108 removeDisableUsers(userIds);
@@ -133,9 +133,9 @@ public class BpmTaskCandidateInvoker { @@ -133,9 +133,9 @@ public class BpmTaskCandidateInvoker {
133 return new HashSet<>(); 133 return new HashSet<>();
134 } 134 }
135 135
  136 + // 1.1 计算任务的候选人
136 Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement); 137 Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement);
137 String param = BpmnModelUtils.parseCandidateParam(flowElement); 138 String param = BpmnModelUtils.parseCandidateParam(flowElement);
138 - // 1.1 计算任务的候选人  
139 Set<Long> userIds = getCandidateStrategy(strategy).calculateUsersByActivity(bpmnModel, activityId, param, 139 Set<Long> userIds = getCandidateStrategy(strategy).calculateUsersByActivity(bpmnModel, activityId, param,
140 startUserId, processDefinitionId, processVariables); 140 startUserId, processDefinitionId, processVariables);
141 // 1.2 移除被禁用的用户 141 // 1.2 移除被禁用的用户
@@ -161,7 +161,7 @@ public class BpmTaskCandidateInvoker { @@ -161,7 +161,7 @@ public class BpmTaskCandidateInvoker {
161 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIds); 161 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIds);
162 assigneeUserIds.removeIf(id -> { 162 assigneeUserIds.removeIf(id -> {
163 AdminUserRespDTO user = userMap.get(id); 163 AdminUserRespDTO user = userMap.get(id);
164 - return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus()); 164 + return user == null || CommonStatusEnum.isDisable(user.getStatus());
165 }); 165 });
166 } 166 }
167 167
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java
1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate; 1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
2 2
3 import cn.hutool.core.map.MapUtil; 3 import cn.hutool.core.map.MapUtil;
  4 +import cn.hutool.extra.spring.SpringUtil;
4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; 6 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  7 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum;
  8 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other.BpmTaskCandidateAssignEmptyStrategy;
6 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; 9 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy;
7 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; 10 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
8 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; 11 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
  12 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
  13 +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
9 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 14 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
10 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 15 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  16 +import org.flowable.bpmn.model.BpmnModel;
  17 +import org.flowable.bpmn.model.ExtensionElement;
  18 +import org.flowable.bpmn.model.FlowElement;
11 import org.flowable.bpmn.model.UserTask; 19 import org.flowable.bpmn.model.UserTask;
12 import org.flowable.engine.delegate.DelegateExecution; 20 import org.flowable.engine.delegate.DelegateExecution;
  21 +import org.flowable.engine.runtime.ProcessInstance;
13 import org.junit.jupiter.api.BeforeEach; 22 import org.junit.jupiter.api.BeforeEach;
14 import org.junit.jupiter.api.Test; 23 import org.junit.jupiter.api.Test;
15 import org.mockito.Mock; 24 import org.mockito.Mock;
  25 +import org.mockito.MockedStatic;
16 import org.mockito.Spy; 26 import org.mockito.Spy;
  27 +import org.mockito.internal.util.collections.Sets;
17 28
18 -import java.util.Collections;  
19 -import java.util.List;  
20 -import java.util.Map;  
21 -import java.util.Set; 29 +import java.util.*;
22 30
23 import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; 31 import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
24 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; 32 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  33 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
  34 +import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE;
  35 +import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX;
25 import static org.junit.jupiter.api.Assertions.assertEquals; 36 import static org.junit.jupiter.api.Assertions.assertEquals;
26 import static org.mockito.ArgumentMatchers.eq; 37 import static org.mockito.ArgumentMatchers.eq;
27 -import static org.mockito.Mockito.mock;  
28 -import static org.mockito.Mockito.when; 38 +import static org.mockito.Mockito.*;
29 39
30 /** 40 /**
31 * {@link BpmTaskCandidateInvoker} 的单元测试 41 * {@link BpmTaskCandidateInvoker} 的单元测试
@@ -39,44 +49,207 @@ public class BpmTaskCandidateInvokerTest extends BaseMockitoUnitTest { @@ -39,44 +49,207 @@ public class BpmTaskCandidateInvokerTest extends BaseMockitoUnitTest {
39 @Mock 49 @Mock
40 private AdminUserApi adminUserApi; 50 private AdminUserApi adminUserApi;
41 51
  52 + @Mock
  53 + private BpmProcessInstanceService processInstanceService;
  54 +
42 @Spy 55 @Spy
43 - private BpmTaskCandidateStrategy strategy ; 56 + private BpmTaskCandidateStrategy userStrategy;
  57 + @Mock
  58 + private BpmTaskCandidateAssignEmptyStrategy emptyStrategy;
44 59
45 @Spy 60 @Spy
46 - private List<BpmTaskCandidateStrategy> strategyList ; 61 + private List<BpmTaskCandidateStrategy> strategyList;
47 62
48 @BeforeEach 63 @BeforeEach
49 public void setUp() { 64 public void setUp() {
50 - strategy = new BpmTaskCandidateUserStrategy(); // 创建strategy实例  
51 - strategyList = Collections.singletonList(strategy); // 创建strategyList 65 + userStrategy = new BpmTaskCandidateUserStrategy(); // 创建 strategy 实例
  66 + when(emptyStrategy.getStrategy()).thenReturn(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY);
  67 + strategyList = List.of(userStrategy, emptyStrategy); // 创建 strategyList
52 taskCandidateInvoker = new BpmTaskCandidateInvoker(strategyList, adminUserApi); 68 taskCandidateInvoker = new BpmTaskCandidateInvoker(strategyList, adminUserApi);
53 } 69 }
54 70
  71 + /**
  72 + * 场景:成功计算到候选人,但是移除了发起人的用户
  73 + */
55 @Test 74 @Test
56 - public void testCalculateUsersByTask() {  
57 - // 准备参数  
58 - String param = "1,2";  
59 - DelegateExecution execution = mock(DelegateExecution.class);  
60 - // mock 方法(DelegateExecution)  
61 - UserTask userTask = mock(UserTask.class);  
62 - when(execution.getCurrentFlowElement()).thenReturn(userTask);  
63 - when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)))  
64 - .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString());  
65 - when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM)))  
66 - .thenReturn(param);  
67 - // mock 方法(adminUserApi)  
68 - AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)  
69 - .setStatus(CommonStatusEnum.ENABLE.getStatus()));  
70 - AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)  
71 - .setStatus(CommonStatusEnum.ENABLE.getStatus()));  
72 - Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)  
73 - .put(user2.getId(), user2).build();  
74 - when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); 75 + public void testCalculateUsersByTask_some() {
  76 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  77 + // 准备参数
  78 + String param = "1,2";
  79 + DelegateExecution execution = mock(DelegateExecution.class);
  80 + // mock 方法(DelegateExecution)
  81 + UserTask userTask = mock(UserTask.class);
  82 + String processInstanceId = randomString();
  83 + when(execution.getProcessInstanceId()).thenReturn(processInstanceId);
  84 + when(execution.getCurrentFlowElement()).thenReturn(userTask);
  85 + when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)))
  86 + .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString());
  87 + when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM)))
  88 + .thenReturn(param);
  89 + // mock 方法(adminUserApi)
  90 + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
  91 + .setStatus(CommonStatusEnum.ENABLE.getStatus()));
  92 + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
  93 + .setStatus(CommonStatusEnum.ENABLE.getStatus()));
  94 + Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
  95 + .put(user2.getId(), user2).build();
  96 + when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);
  97 + // mock 移除发起人的用户
  98 + springUtilMockedStatic.when(() -> SpringUtil.getBean(BpmProcessInstanceService.class))
  99 + .thenReturn(processInstanceService);
  100 + ProcessInstance processInstance = mock(ProcessInstance.class);
  101 + when(processInstanceService.getProcessInstance(eq(processInstanceId))).thenReturn(processInstance);
  102 + when(processInstance.getStartUserId()).thenReturn("1");
  103 + mockFlowElementExtensionElement(userTask, BpmnModelConstants.USER_TASK_ASSIGN_START_USER_HANDLER_TYPE,
  104 + String.valueOf(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType()));
75 105
76 - // 调用  
77 - Set<Long> results = taskCandidateInvoker.calculateUsersByTask(execution);  
78 - // 断言  
79 - assertEquals(asSet(1L, 2L), results); 106 + // 调用
  107 + Set<Long> results = taskCandidateInvoker.calculateUsersByTask(execution);
  108 + // 断言
  109 + assertEquals(asSet(2L), results);
  110 + }
  111 + }
  112 +
  113 + /**
  114 + * 场景:没有计算到候选人,但是被禁用移除,最终通过 empty 进行分配
  115 + */
  116 + @Test
  117 + public void testCalculateUsersByTask_none() {
  118 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  119 + // 准备参数
  120 + String param = "1,2";
  121 + DelegateExecution execution = mock(DelegateExecution.class);
  122 + // mock 方法(DelegateExecution)
  123 + UserTask userTask = mock(UserTask.class);
  124 + String processInstanceId = randomString();
  125 + when(execution.getProcessInstanceId()).thenReturn(processInstanceId);
  126 + when(execution.getCurrentFlowElement()).thenReturn(userTask);
  127 + when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)))
  128 + .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString());
  129 + when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM)))
  130 + .thenReturn(param);
  131 + // mock 方法(adminUserApi)
  132 + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
  133 + .setStatus(CommonStatusEnum.DISABLE.getStatus()));
  134 + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
  135 + .setStatus(CommonStatusEnum.DISABLE.getStatus()));
  136 + Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
  137 + .put(user2.getId(), user2).build();
  138 + when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);
  139 + // mock 方法(empty)
  140 + when(emptyStrategy.calculateUsersByTask(same(execution), same(param)))
  141 + .thenReturn(Sets.newSet(2L));
  142 + // mock 移除发起人的用户
  143 + springUtilMockedStatic.when(() -> SpringUtil.getBean(BpmProcessInstanceService.class))
  144 + .thenReturn(processInstanceService);
  145 + ProcessInstance processInstance = mock(ProcessInstance.class);
  146 + when(processInstanceService.getProcessInstance(eq(processInstanceId))).thenReturn(processInstance);
  147 + when(processInstance.getStartUserId()).thenReturn("1");
  148 +
  149 + // 调用
  150 + Set<Long> results = taskCandidateInvoker.calculateUsersByTask(execution);
  151 + // 断言
  152 + assertEquals(asSet(2L), results);
  153 + }
  154 + }
  155 +
  156 + /**
  157 + * 场景:没有计算到候选人,但是被禁用移除,最终通过 empty 进行分配
  158 + */
  159 + @Test
  160 + public void testCalculateUsersByActivity_some() {
  161 + try (MockedStatic<BpmnModelUtils> bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) {
  162 + // 准备参数
  163 + String param = "1,2";
  164 + BpmnModel bpmnModel = mock(BpmnModel.class);
  165 + String activityId = randomString();
  166 + Long startUserId = 1L;
  167 + String processDefinitionId = randomString();
  168 + Map<String, Object> processVariables = new HashMap<>();
  169 + // mock 方法(DelegateExecution)
  170 + UserTask userTask = mock(UserTask.class);
  171 + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateStrategy(same(userTask)))
  172 + .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy());
  173 + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateParam(same(userTask)))
  174 + .thenReturn(param);
  175 + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))).thenReturn(userTask);
  176 + // mock 方法(adminUserApi)
  177 + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
  178 + .setStatus(CommonStatusEnum.ENABLE.getStatus()));
  179 + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
  180 + .setStatus(CommonStatusEnum.ENABLE.getStatus()));
  181 + Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
  182 + .put(user2.getId(), user2).build();
  183 + when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);
  184 + // mock 移除发起人的用户
  185 + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignStartUserHandlerType(same(userTask)))
  186 + .thenReturn(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType());
  187 +
  188 + // 调用
  189 + Set<Long> results = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId,
  190 + startUserId, processDefinitionId, processVariables);
  191 + // 断言
  192 + assertEquals(asSet(2L), results);
  193 + }
  194 + }
  195 +
  196 + /**
  197 + * 场景:成功计算到候选人,但是移除了发起人的用户
  198 + */
  199 + @Test
  200 + public void testCalculateUsersByActivity_none() {
  201 + try (MockedStatic<BpmnModelUtils> bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) {
  202 + // 准备参数
  203 + String param = "1,2";
  204 + BpmnModel bpmnModel = mock(BpmnModel.class);
  205 + String activityId = randomString();
  206 + Long startUserId = 1L;
  207 + String processDefinitionId = randomString();
  208 + Map<String, Object> processVariables = new HashMap<>();
  209 + // mock 方法(DelegateExecution)
  210 + UserTask userTask = mock(UserTask.class);
  211 + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateStrategy(same(userTask)))
  212 + .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy());
  213 + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateParam(same(userTask)))
  214 + .thenReturn(param);
  215 + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))).thenReturn(userTask);
  216 + // mock 方法(adminUserApi)
  217 + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
  218 + .setStatus(CommonStatusEnum.DISABLE.getStatus()));
  219 + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
  220 + .setStatus(CommonStatusEnum.DISABLE.getStatus()));
  221 + Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
  222 + .put(user2.getId(), user2).build();
  223 + when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);
  224 + // mock 方法(empty)
  225 + when(emptyStrategy.calculateUsersByActivity(same(bpmnModel), eq(activityId),
  226 + eq(param), same(startUserId), same(processDefinitionId), same(processVariables)))
  227 + .thenReturn(Sets.newSet(2L));
  228 +
  229 + // 调用
  230 + Set<Long> results = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId,
  231 + startUserId, processDefinitionId, processVariables);
  232 + // 断言
  233 + assertEquals(asSet(2L), results);
  234 + }
  235 + }
  236 +
  237 + private static void mockFlowElementExtensionElement(FlowElement element, String name, String value) {
  238 + if (value == null) {
  239 + return;
  240 + }
  241 + ExtensionElement extensionElement = new ExtensionElement();
  242 + extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);
  243 + extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);
  244 + extensionElement.setElementText(value);
  245 + extensionElement.setName(name);
  246 + // mock
  247 + Map<String, List<ExtensionElement>> extensionElements = element.getExtensionElements();
  248 + if (extensionElements == null) {
  249 + extensionElements = new LinkedHashMap<>();
  250 + }
  251 + extensionElements.put(name, Collections.singletonList(extensionElement));
  252 + when(element.getExtensionElements()).thenReturn(extensionElements);
80 } 253 }
81 254
82 @Test 255 @Test