Apache Camel Prism路由测试:Mock端点及AdviceWith报错解决
Apache Camel路由测试完整覆盖与AdviceWith错误解决方案
问题背景
你当前的测试用例仅模拟了从SEDA发送端点到Mock端点的简单消息传输,完全未覆盖实际Prism路由中的多播、线程池、内容路由等核心逻辑。同时使用AdviceWith时触发IllegalArgumentException,错误提示找不到可Advice的路由。
核心问题分析
- 测试未加载实际路由:测试类重写
createRouteBuilder时返回的是自定义简化版路由,而非真实的Prism路由类,导致Camel上下文不存在目标路由(routeId为Endpoints.SEDA_SEND_ENDPOINT) - AdviceWith使用错误:你尝试Mock的是发送端点而非目标处理端点,且AdviceWith需要针对已存在的路由进行修改,找不到对应路由就会报错
- 未处理业务依赖:实际路由中的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
相关产品推荐
相关产品推荐

