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操作。
可以通过以下步骤修复:
每个测试前重置Camel上下文
重写CamelQuarkusTestSupport的beforeEach方法,确保每个测试都拥有干净的上下文环境:@Override public void beforeEach(TestInfo testInfo) throws Exception { super.beforeEach(testInfo); // 重新创建上下文并绑定依赖 context = createCamelContext(); context.getRegistry().bind("dispatcherProcessor", dispatcherProcessor); // 重新添加目标路由构建器 addRouteBuilder(new EngineToConnectorRouteBuilder()); }移除重复的上下文启动调用
startCamelContext()方法已经包含启动逻辑,无需再调用context().start(),重复调用会引发状态异常。测试后清理资源
重写afterEach方法,确保上下文正确关闭,避免资源泄漏:@Override public void afterEach(TestInfo testInfo) throws Exception { super.afterEach(testInfo); if (context != null && context.isStarted()) { context.stop(); } }抽离公共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

