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

Apache Camel Prism路由测试:Mock端点及AdviceWith报错解决

Apache Camel路由测试完整覆盖与AdviceWith错误解决方案

问题背景

你当前的测试用例仅模拟了从SEDA发送端点到Mock端点的简单消息传输,完全未覆盖实际Prism路由中的多播、线程池、内容路由等核心逻辑。同时使用AdviceWith时触发IllegalArgumentException,错误提示找不到可Advice的路由。

核心问题分析

  1. 测试未加载实际路由:测试类重写createRouteBuilder时返回的是自定义简化版路由,而非真实的Prism路由类,导致Camel上下文不存在目标路由(routeId为Endpoints.SEDA_SEND_ENDPOINT)
  2. AdviceWith使用错误:你尝试Mock的是发送端点而非目标处理端点,且AdviceWith需要针对已存在的路由进行修改,找不到对应路由就会报错
  3. 未处理业务依赖:实际路由中的Predicate和业务Bean(如CostIQService)需要Mock,避免测试依赖外部服务

解决方案步骤

1. 加载实际Prism路由

将测试类的createRouteBuilder替换为返回真实的Prism路由实例,同时提前注册路由依赖的Bean(如executorService、业务Bean)到Camel上下文。

2. 正确使用AdviceWith Mock目标端点

针对SEDA_PROCESS_ENDPOINT进行Mock,确保在路由启动前执行AdviceWith修改逻辑。

3. Mock业务Bean与Predicate

用Mockito Mock所有业务Bean,避免实际调用;通过设置Header值触发对应的内容路由分支,无需Mock Predicate本身。

4. 编写多分支覆盖测试

针对每个Predicate分支(costIQ、optimization、markDown、pricing)分别编写测试,验证消息流向和Bean调用情况。

完整测试代码示例

package com.sams;

import org.apache.camel.builder.AdviceWith;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.test.junit5.CamelTestSupport;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

import com.sams.pricing.prism.data.processor.PrismRouteBuilder; // 替换为实际路由类名
import com.sams.pricing.prism.data.processor.util.CommonUtility;
import com.sams.pricing.prism.data.processor.util.Endpoints;
import com.sams.pricing.prism.data.processor.service.CostIQService;
import com.sams.pricing.prism.data.processor.service.OptimizationService;
import com.sams.pricing.prism.data.processor.service.MarkDownService;
import com.sams.pricing.prism.data.processor.service.PricingService;
import com.sams.pricing.prism.data.processor.transformer.CostIQPayloadTransformer;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CamelRouteTests extends CamelTestSupport {

    @Mock
    private CostIQService costIQService;
    @Mock
    private OptimizationService optimizationService;
    @Mock
    private MarkDownService markDownService;
    @Mock
    private PricingService pricingService;
    @Mock
    private CostIQPayloadTransformer payloadTransformer;

    private ExecutorService executorService = Executors.newFixedThreadPool(10);

    @Override
    protected void doPreSetup() throws Exception {
        // 初始化Mockito Mock实例
        MockitoAnnotations.openMocks(this);
        // 注册Mock Bean到Camel注册表
        context.getRegistry().bind("costIQService", costIQService);
        context.getRegistry().bind("optimizationService", optimizationService);
        context.getRegistry().bind("markDownService", markDownService);
        context.getRegistry().bind("pricingService", pricingService);
        context.getRegistry().bind("costIQPayloadTransformer", payloadTransformer);
        context.getRegistry().bind("executorService", executorService);
    }

    @Override
    protected RouteBuilder createRouteBuilder() {
        // 返回实际的Prism路由实例
        return new PrismRouteBuilder();
    }

    @Test
    public void testCostIQRoute() throws Exception {
        // 在路由启动前用AdviceWith替换SEDA_PROCESS_ENDPOINT为Mock
        AdviceWith.adviceWith(context, Endpoints.SEDA_SEND_ENDPOINT, a -> {
            a.mockEndpoints(Endpoints.SEDA_PROCESS_ENDPOINT);
        });

        // 手动启动上下文(默认CamelTestSupport会自动启动,需手动控制顺序)
        context.start();

        // 预期消息数:multicast默认复制1份,分支内to三次,共3条
        String mockUri = "mock:" + Endpoints.SEDA_PROCESS_ENDPOINT;
        getMockEndpoint(mockUri).expectedMessageCount(3);

        // 设置Header触发costIQPredicate分支
        template.sendBodyAndHeader(Endpoints.SEDA_SEND_ENDPOINT, "test payload",
                CommonUtility.COST_IQ_HEADER, "true");

        // 验证Mock端点预期是否满足,同时验证Bean调用
        assertMockEndpointsSatisfied();
        Mockito.verify(costIQService).calculatePrice(Mockito.any());
        Mockito.verify(payloadTransformer).payloadTransformer(Mockito.any());
    }

    @Test
    public void testOptimizationRoute() throws Exception {
        AdviceWith.adviceWith(context, Endpoints.SEDA_SEND_ENDPOINT, a -> {
            a.mockEndpoints(Endpoints.SEDA_PROCESS_ENDPOINT);
        });

        context.start();

        String mockUri = "mock:" + Endpoints.SEDA_PROCESS_ENDPOINT;
        getMockEndpoint(mockUri).expectedMessageCount(3);

        template.sendBodyAndHeader(Endpoints.SEDA_SEND_ENDPOINT, "test payload",
                CommonUtility.OPTIMIZATION_HEADER, "true");

        assertMockEndpointsSatisfied();
        Mockito.verify(optimizationService).calculatePrice(Mockito.any());
        Mockito.verify(payloadTransformer).payloadTransformer(Mockito.any());
    }

    // 同理编写markDown和pricing分支的测试用例

    @Override
    protected void doTearDown() throws Exception {
        executorService.shutdown();
        super.doTearDown();
    }
}

关键注意事项

  • 路由加载正确性:必须返回真实路由类,否则测试无法覆盖核心逻辑
  • AdviceWith执行时机:务必在路由启动前执行AdviceWith,否则修改不会生效
  • 消息数计算:根据路由中的multicast、to次数等逻辑,准确设置预期消息数
  • Bean Mock验证:通过Mockito验证业务Bean是否被正确调用,确保路由逻辑符合预期

内容的提问来源于stack exchange,提问作者John

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 18:51:22