基于JBPMJUnitBaseTestCase的调用活动Mock单元测试方案咨询
Mock JBPM Call Activity in JUnit Tests (Without Triggering Subprocess)
我之前在做JBPM流程单元测试时,正好遇到过和你一模一样的需求——想Mock掉调用活动,让主流程测试完全独立于子流程。下面几个方案都是我实际用过的,亲测有效:
方案1:自定义ProcessEventListener拦截调用活动
JBPM的事件监听机制可以让我们在流程节点执行前后介入,刚好可以用来拦截CallActivity的执行,直接返回Mock结果而不启动子流程。
实现步骤:
- 写一个自定义的
ProcessEventListener,在节点触发前判断是否是CallActivity,如果是,就手动设置返回变量并完成该节点:
public class MockCallActivityListener implements ProcessEventListener { private Map<String, Object> mockResults; public MockCallActivityListener(Map<String, Object> mockResults) { this.mockResults = mockResults; } @Override public void beforeNodeTriggered(ProcessNodeTriggeredEvent event) { Node node = event.getNodeInstance().getNode(); // 判断当前节点是否是CallActivity if (node instanceof CallActivityNode) { ProcessInstance processInstance = event.getProcessInstance(); // 将Mock结果设置到主流程变量中(要和子流程返回的变量名一致) mockResults.forEach(processInstance::setVariable); // 手动完成这个CallActivity节点,避免启动子流程 ((WorkflowProcessInstance) processInstance).signal(node.getName()); } } // 其他未用到的接口方法可以留空或者默认实现 @Override public void afterNodeTriggered(ProcessNodeTriggeredEvent event) {} @Override public void beforeNodeLeft(ProcessNodeLeftEvent event) {} @Override public void afterNodeLeft(ProcessNodeLeftEvent event) {} @Override public void beforeProcessStarted(ProcessStartedEvent event) {} @Override public void afterProcessStarted(ProcessStartedEvent event) {} @Override public void beforeProcessCompleted(ProcessCompletedEvent event) {} @Override public void afterProcessCompleted(ProcessCompletedEvent event) {} }
- 在你的JUnit测试类中注册这个监听器:
public class MainProcessTest extends JBPMJUnitBaseTestCase { @Override protected void setup() { super.setup(); // 准备Mock的返回结果,比如子流程要返回的serviceResponse变量 Map<String, Object> mockSubprocessResults = new HashMap<>(); mockSubprocessResults.put("serviceResponse", "Mocked REST Response"); // 注册自定义监听器 getRuntimeEngine().getKieSession().addEventListener(new MockCallActivityListener(mockSubprocessResults)); } @Test public void testMainProcessWithMockedCallActivity() { // 启动主流程 ProcessInstance processInstance = runtimeManager.startProcess("main-process-id"); // 完成用户任务(这里假设用户任务的id是"user-task-1") List<TaskSummary> tasks = getTaskService().getTasksByProcessInstanceId(processInstance.getId()); TaskSummary userTask = tasks.stream().filter(t -> t.getName().equals("User Task")).findFirst().get(); getTaskService().complete(userTask.getId(), "admin", Collections.emptyMap()); // 此时CallActivity会被监听器拦截,直接设置Mock结果,主流程继续执行 assertProcessInstanceCompleted(processInstance.getId()); // 验证主流程是否正确接收了Mock的变量 Object response = getRuntimeEngine().getKieSession().getVariable(processInstance.getId(), "serviceResponse"); assertEquals("Mocked REST Response", response); } }
方案2:用MockWorkItemHandler替换CallActivity的处理器
JBPM中CallActivity本质上是一种WorkItem,所以我们可以注册一个Mock的WorkItemHandler来处理它,直接返回Mock结果。
实现步骤:
- 实现一个Mock的WorkItemHandler:
public class MockCallActivityWorkItemHandler implements WorkItemHandler { private Map<String, Object> mockResults; public MockCallActivityWorkItemHandler(Map<String, Object> mockResults) { this.mockResults = mockResults; } @Override public void executeWorkItem(WorkItem workItem, WorkItemManager manager) { // 直接将Mock结果作为WorkItem的输出返回 manager.completeWorkItem(workItem.getId(), mockResults); } @Override public void abortWorkItem(WorkItem workItem, WorkItemManager manager) { manager.abortWorkItem(workItem.getId()); } }
- 在测试类中注册这个Handler,指定处理
CallActivity类型的WorkItem:
public class MainProcessTest extends JBPMJUnitBaseTestCase { @Override protected void setup() { super.setup(); Map<String, Object> mockSubprocessResults = new HashMap<>(); mockSubprocessResults.put("serviceResponse", "Mocked REST Response"); // 注册Mock WorkItemHandler,处理"CallActivity"类型的任务 getWorkItemManager().registerWorkItemHandler("CallActivity", new MockCallActivityWorkItemHandler(mockSubprocessResults)); } @Test public void testMainProcessWithMockWorkItem() { ProcessInstance processInstance = runtimeManager.startProcess("main-process-id"); // 完成用户任务 List<TaskSummary> tasks = getTaskService().getTasksByProcessInstanceId(processInstance.getId()); TaskSummary userTask = tasks.stream().filter(t -> t.getName().equals("User Task")).findFirst().get(); getTaskService().complete(userTask.getId(), "admin", Collections.emptyMap()); // CallActivity会被Mock Handler处理,直接返回结果 assertProcessInstanceCompleted(processInstance.getId()); Object response = getRuntimeEngine().getKieSession().getVariable(processInstance.getId(), "serviceResponse"); assertEquals("Mocked REST Response", response); } }
方案3:修改流程定义(适合固定Mock场景)
如果你的Mock逻辑比较固定,也可以在测试时临时修改主流程的CallActivity节点,比如把它替换成一个脚本任务,直接设置Mock变量。不过这个方法需要操作BPMN模型,灵活性不如前两个,但适合一些简单场景:
@Test public void testMainProcessWithModifiedProcessDef() { // 加载主流程定义 KieBase kieBase = createKieBaseFromResource("main-process.bpmn"); // 获取流程定义 ProcessDefinition processDef = kieBase.getProcessDefinition("main-process-id"); WorkflowProcessDefinition workflowDef = (WorkflowProcessDefinition) processDef; // 找到CallActivity节点 Node callActivityNode = workflowDef.getNode("call-subprocess-node-id"); if (callActivityNode != null) { // 移除原节点 workflowDef.removeNode(callActivityNode); // 创建一个脚本任务节点,设置Mock变量 ScriptTaskNode scriptNode = new ScriptTaskNode(); scriptNode.setName("Mocked Call Activity"); scriptNode.setId("mock-call-activity"); // 设置脚本内容,比如设置serviceResponse变量 scriptNode.setScript("kcontext.setVariable(\"serviceResponse\", \"Mocked REST Response\");"); scriptNode.setScriptLanguage("juel"); // 将脚本任务插入到原CallActivity的位置(需要根据流程的序列流调整) workflowDef.addNode(scriptNode); // 调整序列流,让用户任务之后指向这个脚本任务,脚本任务之后指向主流程后续节点 // ... 这里需要手动调整序列流的源和目标,比较繁琐,所以只适合简单流程 } // 用修改后的流程定义创建RuntimeManager runtimeManager = createRuntimeManager(kieBase); // 后续测试步骤和之前一致... }
注意事项
- 不管用哪种方案,都要确保Mock的变量名和子流程实际返回的变量名完全一致,不然主流程会找不到变量。
- 如果你用的是JBPM 7+版本,部分API可能有小变化,但核心逻辑是一样的。
- 方案1和方案2更推荐,尤其是方案2,因为它更贴合JBPM的WorkItem机制,代码也更简洁。
内容的提问来源于stack exchange,提问作者Vinoth Nivas
相关产品推荐
相关产品推荐

