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

Quarkus Camel测试中重复AdviceWith路由报错:无可用路由

问题

我曾在测试类中使用String isMockEndpointsAndSkip() { return ("*") }进行测试,但导致其他类抛出异常java.lang.IllegalArgumentException: Cannot advice route as there are no routes。环境版本:quarkus-core 2.16.9.Final、quarkus-camel-bom 2.16.9.Final。

之后我把测试类改成两个包含AdviceWith语句的测试方法,第一个测试通过,但第二个失败。开启quarkus.log.category."org.apache.camel".level=DEBUG调试后发现,第一个测试结束后Camel上下文似乎被关闭了。

测试类代码如下:

@QuarkusTest
public class DispatcherProcessorTest extends CamelQuarkusTestSupport {

    @Inject
    DispatcherProcessor dispatcherProcessor;

    @Override
    public boolean isUseAdviceWith() {
        return true;
    }

    /**
     * Tests that when a "recipient setting" doesn't contain an RBAC group's
     * UUID, the exchange is sent to the {@link Routes#FETCH_USERS} route.
     */
    @Test
    void testSendFetchUsers() throws Exception {
        // Create an exchange with an empty body.
        final Exchange exchange = this.createExchangeWithBody("");

        // Create a recipient settings object without the group's UUID.
        final RecipientSettings recipientSettings = new RecipientSettings(
            true,
            true,
            null,
            new HashSet<>()
        );

        // Set the property that will be grabbed in the processor.
        exchange.setProperty(ExchangeProperty.RECIPIENT_SETTINGS, List.of(recipientSettings));

        // Assert that the exchange was sent to the correct route.
        AdviceWith.adviceWith(this.context(), EngineToConnectorRouteBuilder.ENGINE_TO_CONNECTOR, a -> {
            a.mockEndpointsAndSkip(String.format("direct:%s", Routes.FETCH_USERS));
            a.mockEndpointsAndSkip(String.format("direct:%s", Routes.FETCH_GROUP));
        });
        this.startCamelContext();
        this.context().start();

        final MockEndpoint fetchUsersEndpoint = this.getMockEndpoint(String.format("mock:direct:%s", Routes.FETCH_USERS));
        final MockEndpoint fetchGroupEndpoint = this.getMockEndpoint(String.format("mock:direct:%s", Routes.FETCH_GROUP));

        fetchUsersEndpoint.expectedMessageCount(1);
        fetchGroupEndpoint.expectedMessageCount(0);

        this.dispatcherProcessor.process(exchange);

        fetchUsersEndpoint.assertIsSatisfied();
        fetchGroupEndpoint.assertIsSatisfied();

        // Make sure that the exchange contains the "RecipientSettings" object
        final List<Exchange> exchanges = fetchUsersEndpoint.getExchanges();
        final Exchange sentExchange = exchanges.get(0);
        final RecipientSettings sentRecipientSettings = sentExchange.getProperty(ExchangeProperty.CURRENT_RECIPIENT_SETTINGS, RecipientSettings.class);

        Assertions.assertEquals(recipientSettings, sentRecipientSettings, "the recipient settings object was not properly set in the dispatcher");
    }

    /**
     * Tests that when a "recipient setting" contains an RBAC group's UUID,
     * the exchange is sent to the {@link Routes#FETCH_GROUP} route.
     */
    @Test
    void testSendFetchGroup() throws Exception {
        // Create an exchange with an empty body.
        final Exchange exchange = this.createExchangeWithBody("");

        // Create a recipient settings object with the group's UUID.
        final RecipientSettings recipientSettings = new RecipientSettings(
            true,
            true,
            UUID.randomUUID(),
            new HashSet<>()
        );

        // Set the property that will be grabbed in the processor.
        exchange.setProperty(ExchangeProperty.RECIPIENT_SETTINGS, List.of(recipientSettings));

        // Assert that the exchange was sent to the correct route.
        AdviceWith.adviceWith(this.context(), EngineToConnectorRouteBuilder.ENGINE_TO_CONNECTOR, a -> {
            a.mockEndpointsAndSkip(String.format("direct:%s", Routes.FETCH_USERS));
            a.mockEndpointsAndSkip(String.format("direct:%s", Routes.FETCH_GROUP));
        });
        this.startCamelContext();
        this.context().start();

        final MockEndpoint fetchUsersEndpoint = this.getMockEndpoint(String.format("mock:direct:%s", Routes.FETCH_USERS));
        final MockEndpoint fetchGroupEndpoint = this.getMockEndpoint(String.format("mock:direct:%s", Routes.FETCH_GROUP));

        fetchUsersEndpoint.expectedMessageCount(0);
        fetchGroupEndpoint.expectedMessageCount(1);

        this.dispatcherProcessor.process(exchange);

        fetchUsersEndpoint.assertIsSatisfied();
        fetchGroupEndpoint.assertIsSatisfied();

        // Make sure that the exchange contains the "RecipientSettings" object
        final List<Exchange> exchanges = fetchGroupEndpoint.getExchanges();
        final Exchange sentExchange = exchanges.get(0);
        final RecipientSettings sentRecipientSettings = sentExchange.getProperty(ExchangeProperty.CURRENT_RECIPIENT_SETTINGS, RecipientSettings.class);

        Assertions.assertEquals(recipientSettings, sentRecipientSettings, "the recipient settings object was not properly set in the dispatcher");
    }
}

但第二个测试还是抛出相同异常,原本以为是重复通知同一路由导致,但确认这是允许的,请问哪里操作有误?

解决方案

问题核心是Quarkus测试中Camel上下文在单个测试方法结束后会被销毁,你的代码没有在每个测试前重新初始化上下文和路由,导致第二个测试执行时上下文已关闭,无法完成AdviceWith操作。

可以通过以下步骤修复:

  1. 每个测试前重置Camel上下文
    重写CamelQuarkusTestSupport的beforeEach方法,确保每个测试都拥有干净的上下文环境:

    @Override
    public void beforeEach(TestInfo testInfo) throws Exception {
        super.beforeEach(testInfo);
        // 重新创建上下文并绑定依赖
        context = createCamelContext();
        context.getRegistry().bind("dispatcherProcessor", dispatcherProcessor);
        // 重新添加目标路由构建器
        addRouteBuilder(new EngineToConnectorRouteBuilder());
    }
    
  2. 移除重复的上下文启动调用
    startCamelContext()方法已经包含启动逻辑,无需再调用context().start(),重复调用会引发状态异常。

  3. 测试后清理资源
    重写afterEach方法,确保上下文正确关闭,避免资源泄漏:

    @Override
    public void afterEach(TestInfo testInfo) throws Exception {
        super.afterEach(testInfo);
        if (context != null && context.isStarted()) {
            context.stop();
        }
    }
    
  4. 抽离公共AdviceWith逻辑(可选)
    把重复的Mock配置抽成公共方法,简化代码:

    private void setupRouteMocks() throws Exception {
        AdviceWith.adviceWith(this.context(), EngineToConnectorRouteBuilder.ENGINE_TO_CONNECTOR, a -> {
            a.mockEndpointsAndSkip(String.format("direct:%s", Routes.FETCH_USERS));
            a.mockEndpointsAndSkip(String.format("direct:%s", Routes.FETCH_GROUP));
        });
    }
    

    之后在每个测试方法中调用setupRouteMocks()即可。

修改后,每个测试方法都会拥有独立的Camel上下文,不会因前一个测试关闭上下文而受影响,即可解决第二个测试抛出的异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 12:18:10