You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于JBPMJUnitBaseTestCase的调用活动Mock单元测试方案咨询

Mock JBPM Call Activity in JUnit Tests (Without Triggering Subprocess)

我之前在做JBPM流程单元测试时,正好遇到过和你一模一样的需求——想Mock掉调用活动,让主流程测试完全独立于子流程。下面几个方案都是我实际用过的,亲测有效:

方案1:自定义ProcessEventListener拦截调用活动

JBPM的事件监听机制可以让我们在流程节点执行前后介入,刚好可以用来拦截CallActivity的执行,直接返回Mock结果而不启动子流程。

实现步骤:

  1. 写一个自定义的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) {}
}
  1. 在你的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结果。

实现步骤:

  1. 实现一个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());
    }
}
  1. 在测试类中注册这个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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 21:32:39