23

假设我在单独的 RouteBuilder 类中创建了我的路线。看起来像:

  • 从 JMS 队列中获取消息
  • 做一些转换,验证等
  • 根据验证结果转发到特定的 JMS 队列并将某些内容保存在 DB 中

我想在没有 JMS 代理和数据库的情况下对这条路线进行单元测试。我知道我可以模拟我的处理器实现,但这还不够。我不想改变这条路线(假设我在 jar 文件中得到了那个类)。据我从 Camel in Action(第 6.2.6 节)中了解到,为了能够使用端点模拟和其他东西,我需要更改我的路由端点定义(在本书的示例中,这是“mina:tcp:/ /miranda”到“mock:miranda”等)。

是否可以在不更改路由定义的情况下完全隔离地测试流程?如果我将 RouteBuilder 作为一个单独的类,我是否被迫以某种方式“复制”路由定义并手动更改它?不是测试错了吗?

我对骆驼很陌生,对我来说,能够在开发路线的同时进行独立的单元测试真的很酷。只是为了能够改变一些东西,运行小测试,观察结果等等。

4

4 回答 4

25

假设 RouteBuilder 类具有硬编码的端点,那么它的测试就有点困难了。但是,如果 RouteBuilder 使用属性占位符作为端点 uri,那么您通常可以使用一组不同的端点 uri 进行单元测试。如骆驼书的第 6 章所述。

如果它们是硬编码的,那么您可以在单元测试中使用带有功能的建议,如下所示:https ://camel.apache.org/components/latest/others/test-cdi.html#CDITesting-RoutesadvisingwithadviceWith

在 Camel 2.7 中,我们可以更轻松地操纵路线,因此您可以移除部件、更换部件等。这就是链接所讨论的编织内容。

例如,要模拟向数据库端点发送消息,您可以使用上面的方法并将 to 替换为您将其发送到模拟的另一个位置。

在以前的版本中,您可以使用在 Camel 书(第 6.3.3 节)中介绍的 interceptSendToEndpoint 技巧

哦,您也可以使用模拟组件替换组件,如第 169 页所示。现在在 Camel 2.8 以后,模拟组件将不再抱怨它不知道的 uri 参数。这意味着在每个组件级别上用模拟替换组件要容易得多。

于 2011-04-14T15:10:36.713 回答
6

我有

   <bean id="properties" class="org.apache.camel.component.properties.PropertiesComponent">
        <property name="location" value="classpath:shop.properties"/>
    </bean>

    <route>
        <from uri="direct://stock"/>
        <to uri="{{stock.out}}"/>
    </route>

在我的 spring 文件中,然后在测试类路径上的 shop.properties 中,我有一个 stock.out=xxxx ,它在运行时被替换,所以我可以有不同的路由,一个用于运行时,一个用于测试

6.1.6 多环境单元测试中有一个更好的例子

于 2011-04-14T15:15:45.683 回答
4

虽然您可以根据 Claus Ibsen 的回答使用拦截和建议来交换端点,但我认为让您的路由接受实例要好得多,Endpoint 这样您的测试就不会与您的生产端点 URI 耦合。

例如,假设您有一个RouteBuilder看起来像

public class MyRoute extends RouteBuilder {
    @Override
    public void configure() throws Exception {
        from("http://someapi/someresource")
        .process(exchange -> {
            // Do stuff with exchange
        })
        .to("activemq:somequeue");
    }
}

您可以像这样注入端点:

public class MyRoute extends RouteBuilder {
    private Endpoint in;
    private Endpoint out;

    // This is the constructor your production code can call
    public MyRoute(CamelContext context) {
        this.in = context.getEndpoint("http://someapi/someresource");
        this.out = context.getEndpoint("activemq:somequeue");
    }

    // This is the constructor your test can call, although it would be fine
    // to use in production too
    public MyRoute(Endpoint in, Endpoint out) {
        this.in = in;
        this.out = out;
    }

    @Override
    public void configure() throws Exception {
        from(this.in)
        .process(exchange -> {
            // Do stuff with exchange
        })
        .to(this.out);
    }
}

然后可以像这样测试:

public class MyRouteTest {
    private Endpoint in;
    private MockEndpoint out;
    private ProducerTemplate producer;

    @Before
    public void setup() {
        CamelContext context = new DefaultCamelContext();

        this.in = context.getEndpoint("direct:in");
        this.out = context.getEndpoint("mock:direct:out", MockEndpoint.class);
        this.producer = context.createProducerTemplate();
        this.producer.setDefaultEndpoint(this.in);

        RouteBuilder myRoute = new MyRoute(this.in, this.out);
        context.addRoutes(myRoute);

        context.start();
    }

    @Test
    public void test() throws Exception {
        this.producer.sendBody("Hello, world!");
        this.out.expectedMessageCount(1);
        this.out.assertIsSatisfied();
    }
} 

这具有以下优点:

  • 您的测试非常简单易懂,甚至不需要扩展CamelTestSupport或其他辅助类
  • CamelContext是手动创建的,因此您可以确保仅创建被测路由
  • 测试不关心生产路由 URI
  • 如果需要,您仍然可以方便地将端点 URI 硬编码到路由类中
于 2017-09-15T14:52:58.200 回答
0
  1. 如果您使用的是 Spring(这主要是一个好主意),我想分享我的方法。

    您的生产路线是一个带有特殊类 MyRoute 的 Spring bean

    @Component
    public class MyRoute extends RouteBuilder {
    
        public static final String IN = "jms://inqueue";
    
        @Override
        public void configure() throws Exception {
            from(IN)
            .process(exchange -> {
                // Do stuff with exchange
            })
            .to("activemq:somequeue");
        }
    }
    

    所以在测试中你可以像这样轻松地覆盖它(这是一个spring java config internal(对测试类)类):

    static class TestConfig extends IntegrationTestConfig {
    
            @Bean
            public MyRoute myRoute(){
                return new MyRoute() {
                    @Override
                    public void configure() throws Exception {
                        interceptFrom(MyRoute.IN)
                                .choice() 
    
                                    .when(x -> delayThisMessagePredicate.matches(x)) //make the predicate modifiable between tests
                                    .to("log:delayed")
                                    .delay(5000)
                                .endChoice();
                        super.configure();
                    }
                };
            }
    }
    

    请注意 super.configure() 安装您的生产路线,您可以使用interceptFrom、interceptSendToEndpoint 来注入测试代码:例如引发异常。

    我还添加了一些辅助路线。通过这条路线,我可以测试,在输出文件夹中生成了一个文件,它可能是一个 JMS 使用者......

        @Bean
        public RouteBuilder createOutputRoute() {
    
            return new RouteBuilder() {
                @Override
                public void configure() {
    
                    fromF(FILE_IN,
                            outputDir)
                            .to("mock:output")
                            .routeId("doneRoute");
            };
    
  2. 对于 JMS/JDBC/... 还有 Mockrunner。在您的测试配置中使用下面的代码,您几乎完成了:您的 JMS 连接工厂被一个模拟实现所取代,所以现在您甚至可以将一些东西放入 JMS 并从 jms 读取(使用上面解释的简单骆驼路线)来验证。不要忘记在模拟上创建一个队列。

    @Bean(JMS_MOCK_CONNECTION_FACTORY) @Primary public ConnectionFactory jmsConnectionFactory() { return (new JMSMockObjectFactory()).getMockQueueConnectionFactory(); }

  3. 我不喜欢 AdviseWith,是的,它很灵活,但是它需要您在测试中手动处理 camelContext,这对我来说太麻烦了。我不想将该代码放在数百个测试中,也不想围绕它创建一个框架。对 CamelTestSupport 进行子类化也可能是一个问题,例如,如果您将使用两个库,这两个库都要求您对某些内容进行子类化,并且您可能有自己的测试类层次结构,但您看不到 CamelTestSupport。我尽量不在我的测试中使用类层次结构以使测试独立。子类化意味着您需要一些魔法才能发生(您不会直接在测试中看到该代码)。如果你修改那个魔法,你会影响很多测试。我为此使用spring java配置集。

于 2020-06-05T23:17:10.907 回答