Camel 3:如何使用“interceptSendToEndpoint”拦截来自“onException”的路由

geg*_*geg 3 testing apache-camel kotlin apache-camel-3

问题:

在从 Camel 2 迁移到 3 的过程中,我的错误路由测试失败了。

我遵循的模式是强制异常并断言该onException()块使用适当的标签发送到我的指标路由。

我正在使用 uri 模式匹配来单独测试每个标签的发出...这强烈影响测试模式

注意:在下面的两个示例中,createRouteBuilder()方法是相同的

传递骆驼2示例

import org.apache.camel.RoutesBuilder
import org.apache.camel.builder.RouteBuilder
import org.apache.camel.test.junit4.CamelTestSupport
import org.junit.Test
import java.util.concurrent.TimeUnit

class Camel2Test : CamelTestSupport() {

    val startUri = "direct:start"
    val baseMetricsUri = "micrometer:counter:errors"
    // Want to use pattern to test each individual tag here
    val fullMetricsUri = "$baseMetricsUri?tags=a=1,b=2"

    override fun isUseAdviceWith(): Boolean {
        return true
    }

    override fun createRouteBuilder(): RoutesBuilder {
        return object : RouteBuilder() {
            override fun configure() {

                onException(Exception::class.java)
                    .to(fullMetricsUri)

                from(startUri)
                    .routeId(startUri)
                    .throwException(Exception())
            }

        }
    }

    @Test
    fun `metric with tag B is emitted`() {
        val exchange = createExchangeWithBody("")

        val mockEndpoint = getMockEndpoint("mock:test")

        context.getRouteDefinition(startUri)
            .adviceWith(context, object : RouteBuilder() {
                override fun configure() {
                    interceptSendToEndpoint("$baseMetricsUri.*b.*2.*") // <-- PATTERN
                        .skipSendToOriginalEndpoint()
                        .to(mockEndpoint)
                }
            })

        context.start()

        mockEndpoint.expectedMessageCount(1)

        template.send(startUri, exchange)

        assertMockEndpointsSatisfied(2, TimeUnit.SECONDS)
    }
}
Run Code Online (Sandbox Code Playgroud)

失败的 Camel 3 示例

import org.apache.camel.RoutesBuilder
import org.apache.camel.builder.AdviceWithRouteBuilder
import org.apache.camel.builder.RouteBuilder
import org.apache.camel.test.junit4.CamelTestSupport
import org.junit.Test
import java.util.concurrent.TimeUnit

class Camel3Test : CamelTestSupport() {

    val startUri = "direct:start"
    val baseMetricsUri = "micrometer:counter:errors"
    // Want to use pattern to test each individual tag here
    val fullMetricsUri = "$baseMetricsUri?tags=a=1,b=2"

    override fun isUseAdviceWith(): Boolean {
        return true
    }

    override fun createRouteBuilder(): RoutesBuilder {
        return object : RouteBuilder() {
            override fun configure() {

                onException(Exception::class.java)
                    .to(fullMetricsUri)

                from(startUri)
                    .routeId(startUri)
                    .throwException(Exception())
            }

        }
    }

    @Test
    fun `metric with tag B is emitted`() {
        val exchange = createExchangeWithBody("")

        val mockEndpoint = getMockEndpoint("mock:test")

        AdviceWithRouteBuilder.adviceWith(context, startUri) { routeBuilder ->
            routeBuilder.interceptSendToEndpoint("$baseMetricsUri.*b.*2.*") // <-- PATTERN
                .skipSendToOriginalEndpoint()
                .to(mockEndpoint)
        }

        context.start()

        mockEndpoint.expectedMessageCount(1)

        template.send(startUri, exchange)

        assertMockEndpointsSatisfied(2, TimeUnit.SECONDS)
    }
}
Run Code Online (Sandbox Code Playgroud)

mockEndpoint接收交换,而是仍发送至指标端点。

问题:

在 Camel 3 中,如何使用模式拦截像在 Camel 2 中那样的路线?手动测试表明错误路由在产品中的行为符合预期,因此这似乎是一个测试配置问题。

其他详情:

  • 来自camel repo的单元测试演示了我正在尝试做的事情,但是通过手动拦截路由而不是mock:直接在路由中使用。
  • 当我不需要模式匹配时,这种替代方法就有效

    override fun isMockEndpointsAndSkip() = myUri
    
    // ... in test
    getMockEndpoint("mock:$myUri").expectedMessageCount(1)
    
    Run Code Online (Sandbox Code Playgroud)

She*_*gon 6

首先,非常感谢您提出了一个框架良好的问题以及正确的代码示例!模拟组件的手册提到了“模拟现有端点”功能的介绍,很可能这就是阻止您的原因。我不太确定Camel的哪个版本引入了这个功能。

无论如何,为了解决当前的限制,您可以使用自动模拟功能本身。您的测试方法可以如下更改以使其正常工作。

 @Test
    fun `exception is routed to error logging route`() {
        val exchange = createExchangeWithBody("")

        // Create new mock endpoint that will replace our error route
        val mockEndpoint = getMockEndpoint("mock:$errorUri") 

        AdviceWithRouteBuilder.adviceWith(context, startUri) { routeBuilder ->
            routeBuilder.mockEndpoints(errorUri) 
            routeBuilder.interceptSendToEndpoint(errorUri)
                    .skipSendToOriginalEndpoint()
                    .to(mockEndpoint)
        }

        context.start()

        mockEndpoint.expectedMessageCount(1)

        template.send(startUri, exchange)

        assertMockEndpointsSatisfied()
    }
Run Code Online (Sandbox Code Playgroud)

对原始代码进行了两处更改。

  1. 模拟端点已重命名,以mock:test符合自动生成的模拟端点类型 ( mock:direct:errors)
  2. 调用routeBuilder.mockEndpoints(errorUri)以便camel可以自动注入Mocks,对于如下所述的模式errorUri

除此之外,还可以替换下面的块

  routeBuilder.mockEndpoints(errorUri)
  routeBuilder.interceptSendToEndpoint(errorUri)
          .skipSendToOriginalEndpoint()
          .to(mockEndpoint)
Run Code Online (Sandbox Code Playgroud)

使用单衬routeBuilder.mockEndpointsAndSkip(errorUri),除非有特定的原因要使用,intercept正如您在问题中提到的那样。

附加观察结果:

在不进行任何更改的情况下运行代码会清楚地显示RouteReifierMock 端点中的挂钩,mock://test而不是direct:errors. 此外,似乎也context有一个适当的。endpointStrategy

这可能是一个错误。尽管有简单的替代方案,但也请考虑将其作为ASF Jira上的问题提出。

14:32:34.307 [main] INFO org.apache.camel.reifier.RouteReifier - Adviced route before/after as XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<route xmlns="http://camel.apache.org/schema/spring" customId="true" id="direct:start">
    <from uri="direct:start"/>
    <onException>
        <exception>java.lang.Exception</exception>
        <to uri="direct:errors"/>
    </onException>
    <throwException/>
</route>

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<route xmlns="http://camel.apache.org/schema/spring" customId="true" id="direct:start">
    <from uri="direct:start"/>
    <onException>
        <exception>java.lang.Exception</exception>
        <to uri="direct:errors"/>
    </onException>
    <interceptSendToEndpoint skipSendToOriginalEndpoint="true" uri="direct:errors">
        <to uri="mock://test"/>
    </interceptSendToEndpoint>
    <throwException/>
</route>
Run Code Online (Sandbox Code Playgroud)

IDE 中测试通过

IDE 的屏幕截图

Java实现(如果有人需要的话)


import org.apache.camel.Exchange;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.builder.AdviceWithRouteBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Assert;
import org.junit.Test;

public class Camel3RouteTest extends CamelTestSupport {

    private static final String startUri = "direct:start";
    private static final String errorUri = "direct:errors";
    private static final String mockErrorURI = "mock:"+ errorUri;
    private static final String ERROR_MESSAGE = "ERROR MESSAGE!";

    @Override
    protected RoutesBuilder createRouteBuilder() throws Exception {
        return new RouteBuilder() {
            @Override
            public void configure() throws Exception {

                onException(Exception.class)
                        .to(errorUri);

                from(errorUri)
                        .routeId(errorUri)
                        .log("error happened!");

                from(startUri)
                        .routeId(startUri)
                        .throwException(new Exception(ERROR_MESSAGE));

            }
        };
    }

    @Test
    public void testExecution() throws Exception {

        AdviceWithRouteBuilder.adviceWith(context, startUri, adviceWithRouteBuilder -> {
            //a.mockEndpointsAndSkip(errorUri);

            adviceWithRouteBuilder.mockEndpoints(errorUri);
            adviceWithRouteBuilder.interceptSendToEndpoint(errorUri).skipSendToOriginalEndpoint().to(mockErrorURI);
        });

        MockEndpoint mockEndpoint = getMockEndpoint(mockErrorURI);
        mockEndpoint.setExpectedMessageCount(1);

        context.start();
        sendBody(startUri, "A Test message");
        assertMockEndpointsSatisfied();

        Assert.assertNotNull(mockEndpoint.getExchanges().get(0).getProperty(Exchange.EXCEPTION_CAUGHT));
        Exception receivedException = (Exception) mockEndpoint.getExchanges().get(0).getProperty(Exchange.EXCEPTION_CAUGHT);

        Assert.assertTrue(receivedException instanceof Exception);
        Assert.assertEquals(receivedException.getMessage(), ERROR_MESSAGE);


    }


}

Run Code Online (Sandbox Code Playgroud)