Apache Camel 测试指南

Apache Camel 测试指南

如何使用 Camel 的测试框架来测试你的路线

Apache Camel 测试指南

今天我想谈谈测试。(人群尖叫着跑开……)

嘿,回来!!测试其实没那么难。一旦您了解了 Camel 测试套件,您实际上可以相对轻松地对 Camel 路由进行单元测试。

Camel 测试套件是一组扩展和 API,允许您测试 Camel 应用程序。它们通过发送消息并检查消息是否按预期接收,使测试您的 Camel 路由变得更容易。在 Camel 的camel-test依赖项中找到测试套件。

编写测试可以相当有趣!测试骆驼路线就像打开香肠机...... 然后验证它实际上是在制作香肠:

img

美味的香肠。🌭

在本文中,我们将介绍在测试 Camel 时需要了解的主要概念,如何为您的项目设置单元测试 香肠机 Camel 路由,查看您可以使用的一些测试 API,以及有关如何编写好的测试的一些建议。

如何测试骆驼路线

要测试 Camel 路线,您需要从 Camel Test Kit 开始。Camel 测试工具包是 JUnit、辅助类和 API 的一组扩展,允许您控制路由、发送测试消息、执行断言等。

测试套件位于camel-testcamel-test-spring依赖项中。

第 1 步:Bootstrap Camel 并添加您的路线

在我们运行测试之前,我们需要启动 Camel Context,并添加我们想要测试的东西(路由和组件)。

为了帮助您做到这一点,Camel 附带了一些抽象的测试辅助类。它们可以让您子类化 ( extend),添加您的路由,然后添加您的测试。

这些辅助类在测试期间管理 Camel 的生命周期。您可以通过覆盖其中一种方法来提供路线。这些类还为您提供了用于处理骆驼上下文 ( CamelContext) 和向骆驼 ( ProducerTemplate)发送消息的对象:

您可能会使用的两个主要帮助类是:

  • org.apache.camel.test.junit4.CamelTestSupport - 一个用于测试 Camel 应用程序的类,您在RouteBuilder. 您RouteBuilder通过覆盖该createRouteBuilder()方法来提供您的。

  • org.apache.camel.test.spring.CamelSpringTestSupport - 用于测试使用 Spring XML DSL 的 Camel 应用程序的类。您可以通过覆盖该createApplicationContext()方法来提供 XML DSL 文件。

通过使用这些类之一,您将获得:

  • 在您运行测试之前启动了一个 Camel 上下文,并使用一个方法来覆盖以提供您的RouteBuilder

  • 测试后正常关闭 Camel Context

  • ProducerTemplate,你可以用它来发送消息给骆驼对象

  • CamelContext你可以用它来修改你的路由对象

  • 您可以在测试期间用于更改 Camel 的各种其他辅助方法

如果您想了解可以使用这些类执行的所有操作,请查看Javadoc或查看源代码。

第 2 步:添加 Mock 组件

模拟组件就像 Camel 中的虚拟组件。模拟组件的目的是让您可以测试 Camel 的行为,而不必访问“真实”的外部系统。

模拟组件就像 Camel 中的任何其他组件一样,只是它们不做任何事情。它们旨在传递消息。

您还可以向模拟组件添加断言和行为。断言允许您测试模拟组件是否收到特定消息。行为允许您配置模拟组件,以便它在特定条件下返回特定消息。如果您希望模拟组件返回真实系统将返回的那种消息,那么您可以将其配置为一种行为。

带有 Direct 组件的 Camel 路由图,然后是 Bean,最后是 Mock。 文本内容为:'Mock 组件收到一条消息,并对其执行断言'带有模拟组件的典型 Camel 路由

例如:假设您编写了一个Camel 路由,它接收来自 ActiveMQ 队列的消息,然后将消息中的数据发送到 Salesforce。当您编写单元测试时,您不想访问“真正的”Salesforce,因此您将用salesforce:模拟替换 Camel 的组件。这允许您单独测试 Camel 的行为。

如何将 Mock 组件添加到 Camel 路由中?您可以尝试以下方法之一:

  • 在测试过程中修改路由,使用 Camel 的AdviceWithRouteBuilder类,或 Camel 的mockEndpointsAndSkip方法- 可以用模拟修改和替换真实组件

  • 使您的端点 URI 可配置- 在您的路由中使用占位符,例如.to("{{my.endpoint}}"),而不是端点 URI。运行测试时,可以将这些属性设置为mock:端点 URI

  • mock:foo在您的 Camel 路由中一样明确指定一个模拟组件 URI - 尽管这意味着mock:在您的路由中硬编码一堆端点,这是不切实际的。

第三步:添加测试用例

现在我们添加一些测试用例。

每个测试用例都是 JUnit 中的一个方法。所以我们创建了一个方法并用@Test.

Camel 测试用例通常遵循这种方法:

  1. Arrange- 我们配置我们的模拟端点。我们做了一些断言,比如“模拟组件将收到 1 条消息”,或“模拟组件将收到 3 次消息‘你好’”。

  2. Act——我们向路由发送消息。这是我们触发路由的方式。作为测试的一部分,我们选择要发送到路由的消息。这可能是路由通常会收到的典型消息,也可能是边缘情况或无效消息,以测试它是否会导致应用程序崩溃。

  3. Assert——我们断言我们的模拟端点已经得到满足。这意味着我们检查我们在第一步中建立的断言是否已被证明是正确的。通常,这意味着我们验证模拟端点是否收到了我们预期的消息。

Camel 路由测试示例(Java DSL)

基于上述步骤,让我们看一个示例,该示例是为使用 Camel 的 Java DSL 的应用程序实现的。

首先,RouteBuilder这是通常会传递给 Camel 的类,用于向上下文添加路由。此处,路由以 Direct 组件开始,然后使用 bean 进行处理,最后发送到mock:端点:

import org.apache.camel.builder.RouteBuilder;
​
public class MyRouteBuilder extends RouteBuilder {
​
    @Override
    public void configure() throws Exception {
        from("direct:start")
                .bean(MyBean.class)
                .to("mock:output"); // Defines a Mock endpoint called 'output'
    }
}

为了编写测试,我们创建了一个扩展CamelTestSupport. 在该类中,我们重写createRouteBuilder为 return MyRouteBuilder,其中包含要测试的路由。

我们还添加了一个测试用例,格式为:Arrange、Act、Assert。它将设置模拟组件断言,将消息发送到路由,然后断言模拟端点得到满足。我们使用抽象类提供的ProducerTemplatetemplatea对象:

import org.apache.camel.test.junit4.CamelTestSupport;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.junit.Test;
​
public class MyQuickCamelTest extends CamelTestSupport {
​
    // We override this method with the routes to be tested
    @Override
    protected RoutesBuilder createRouteBuilder() throws Exception {
        // We override this method and provide our RouteBuilder class
        return new MyRouteBuilder();
    }
​
    // We write a simple JUnit test case
    @Test
    public void testRoute() throws Exception {
        // 1. Arrange.. get the mock endpoint and set an assertion
        MockEndpoint mock = getMockEndpoint("mock:output");
        mockOutput.expectedMessageCount(1);
​
        // 2. Act.. send a message to the start component
        template.sendBody("direct:start", null);
​
        // 3. Assert.. verify that the mock component received 1 message
        assertMockEndpointsSatisfied();
    }
​
}

你有一个简单的测试!我们获取模拟端点,断言它将接收1 条消息,向路由发送一条消息,并验证我们的断言是否正确。

这是在 Apache Camel 中使用模拟组件测试路由的最简单示例。

ProducerTemplate有几种不同的方法可用于发送消息、带有标题的消息等。

Apache Camel 中不同类型的测试

您可能很想开始编写测试,但您可能想考虑要进行哪种类型的测试。

Camel 是一个集成框架。这意味着它旨在与许多不同的系统和应用程序进行交互。您想单独对您的路由进行单元测试,并模拟真实的交互吗?或者您想与真实系统和组件集成?

单元测试

单元测试单独测试一个小的业务逻辑单元。单元测试不应与外部系统(如消息代理或数据库)通信。它应该只是测试代码。

大多数人从单元测试开始,然后向上工作。编写单元测试几乎总是比设置更复杂的东西(如集成测试或系统测试)更容易。

在 Camel 中,您可以使用 Mock 端点来实现这一点。它们允许您测试路由逻辑,而无需支持路由所依赖的外部 API 或组件。模拟端点通过用假或模拟替换路由中的真实组件来实现这一点。

集成和系统测试

作者和软件架构师 Martin Fowler 给出了集成测试的这个定义

集成测试确定独立开发的软件单元在相互连接时是否能正常工作。

对于 Camel,集成测试是一个很难映射的概念。“独立开发的软件单元”是指可以单独测试的 Camel 路由吗?或者我们的意思是测试整个 Camel 上下文?我们正在集成的其他“软件单元”是什么?

就个人而言,我认为集成测试系统测试是测试不同组件如何相互集成。这可能包括针对真实数据库(或其中的测试实例)或真实消息代理进行测试。我也认为这是对应用程序的“跨越边界”的测试。

例如,我认为这两个都是集成测试:

  • 验证我们可以对 Camel 路由进行 HTTP REST 调用的测试,并且 Camel 返回“200”状态代码;因为我们直接访问 HTTP 接口,可能使用 HTTP 客户端。

  • 触发一些 Camel 代码的测试,该代码将消息发送到 ActiveMQ 队列,然后检查真实代理上的队列,以验证消息是否到达

如何指向测试代理、数据库等?要将您的应用程序配置为指向您的测试依赖项(如数据库或消息代理),您可以使用运行 Camel 的容器的属性管理功能,例如 Spring Boot 的application.properties. 例如,您可以将 Camel 配置为与集成测试中的测试消息代理通信。

比较 Camel 中的集成测试和单元测试

下表比较了基于 Apache Camel 的应用程序实现单元测试和集成测试之间的差异:

 单元测试集成/系统测试
测试什么? 仅测试一条骆驼路线 测试整个应用程序
Camel组件的使用 用模拟替换组件 使用实时、真实的组件
与外部系统的连接 没有与外部系统的连接 - 只是模拟 连接到测试实例,例如测试消息代理
测试目的 测试业务逻辑;测试错误(回归);测试边缘或异常情况 测试该组件是否与另一个组件成功集成
在测试中使用 Spring? 大多没有必要 是的,如果您使用 Spring
需要第三方系统吗? 没有任何 您要连接的系统的真实或测试实例
使用的框架和技术 JUnit + Camel 测试套件 JUnit + Camel 测试套件,以及可选的TestcontainersArquillian、RestAssured、Spring Boot Test、Spring 的 RestTemplate……
示例测试 使用 Camel API 向 Camel 路由发送消息(例如调用 Direct 组件),然后断言 Camel 模拟组件收到预期的消息 通过其面向公众的 API(例如 REST API 或接收文件的目录)调用路由,和/或断言第三方系统接收到预期的消息
测试次数 通常更高;大量快速、简单的测试 通常较少;少量更复杂的“端到端”测试
实施难度相对 更轻松 更难

实际上,单元测试和集成之间的界限并不总是那么清晰。

例如:您可以使用 Spring Boot 的测试支持,它引导您所有的 Spring bean 以允许您作为一个完整的整体来测试您的应用程序。然后在您的测试本身中,您仍然可以使用 Camel 测试套件来修改您的路线,或用模拟替换组件。

如何编写出色的单元测试

这是编写好的单元测试的简要回顾

当您编写单元测试时,您希望确保它们对您您今天编写的代码的未来维护者有用。

我总是努力记住:

  • 单元测试应该测试一个契约——用 Camel 的话说,这意味着测试你的路由“边缘”之间的所有东西。这意味着您应该测试Camel 路由的逻辑

  • 单元测试应该独立运行——这意味着一个测试的结果不应该影响另一个。

    或者,换句话说:以不同的顺序运行测试不应该对它们的成功产生任何影响。

  • 单元测试应该随着代码的发展而维护——一旦你编写了一个测试,请确保随着代码的增长而保持它。

    这意味着不要试图使用-DskipTests=true!很调皮!

  • 编写单元测试以帮助未来的开发人员了解您的代码是如何工作的-在描述一段代码的工作方式方面,单元测试可能比文档要好得多。因此,请努力编写合理的、可读的测试。

考虑到这些原则,让我们看看如何为您的 Camel 应用程序设置测试。

其他类型的 Camel 测试

Camel on Karaf(蓝图)测试

如果您正在使用Blueprint 依赖项注入框架——因为您要部署到像 Apache ServiceMix 这样的 OSGi 容器——那么您可以使用Camel 的 Blueprint 测试支持

首先,您需要添加camel-test-blueprint一个依赖项:

<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-test-blueprint</artifactId>
    <version>${camel.version}</version>
    <scope>test</scope>
</dependency>

然后,你需要写一个测试类,其延伸 CamelBlueprintTestSupport

在您的测试类中,您需要覆盖方法getBlueprintDescriptor。返回定义 Camel 上下文的蓝图 XML 定义的路径。这会告诉 Camel 在哪里可以找到您的蓝图文件,以便它可以启动 Camel 上下文。

这是一个让您入门的示例

import org.apache.camel.test.blueprint.CamelBlueprintTestSupport;
​
public class MyCamelAppTest extends CamelBlueprintTestSupport {
​
    @Override
    protected String getBlueprintDescriptor() {
        return "/OSGI-INF/blueprint/camel-context.xml";
    }
​
    @Test
    public void testContextOk() throws Exception {
        // A very simple test...
        // We test that the Camel Context starts correctly. (so our XML is valid!)
        // `context` is the injected CamelContext, provided by CamelBlueprintTestSupport
        assertTrue(context.getStatus().isStarted());
    }  
​
}

如果您还想注入一些配置属性,因为您正在使用OSGi Config Admin service,则覆盖该loadConfigAdminConfigurationFile方法。这会告诉 Camel Blueprint Test 您的测试属性所在的位置:

@Override
protected String[] loadConfigAdminConfigurationFile() {
    return new String[] {
        "src/test/resources/etc/properties.cfg",  // path to your file
        "my-config-pid" // the config admin 'PID' name
    };
}

Camel 测试基本 API

现在您已经设置了您的框架测试类,我将运行您可以在测试中使用的Camel 测试框架的一些功能。

使用模拟端点

首先确定您将要观察的 MockEndpoint。然后,在该端点上设置一些断言——例如,您希望到达那里的消息数量,或者消息的内容应该是什么:

MockEndpoint mock = getMockEndpoint("mock:output");
​
// Assert that 1 message will be received
mock.expectedMessageCount(1);
​
// Assert that 1 message will be received, with the body 'Hiya!' (oh hiya!)
mock.expectedBodiesReceived("Hiya");

然后向端点发送消息...:

// 'template' is a ProducerTemplate, provided by CamelTestSupport
template.sendBody("direct:start", "Hiya");

以断言结束您的测试用例,例如以下之一:

// Assert the endpoint is satisfied within 5 seconds
assertIsSatisfied(5L, TimeUnit.SECONDS, mock);

向端点发送数据

从文件向端点发送数据:

template.sendBody("direct:start", new File("src/test/data/chickens.xml"));

Camel测试最佳实践

我认为值得分享我个人的最佳实践。这些是我在为 Camel 编写测试时使用的做法:

  • 将复杂的路由分成更小的路由,然后使用Direct 端点或类似的胶水将它们连接在一起。这使您可以更轻松地测试您的业务逻辑。

    例如 - 使用下面的路由,我们可以单独测试processCustomer路由,而不必担心 Web 服务调用或 File 组件:

    from("direct:start")
        .split()
        .to("direct:processCustomer") // Call another route which contains some logic
        .to("http://example.com/webservice")
        .to("file:customers/processed");
    ​
    // We can more easily test this route because
    // it just contains logic, and no interactions with other systems
    from("direct:processCustomer")
        .bean(ProcessCustomer.class)
        .bean(SaveCustomer.class);
  • 模拟替换端点- Camel 中的模拟端点确实为您提供了超能力。它们提供了各种方法来测试消息是否已到达、是否符合特定条件、消息是否以正确的顺序到达……您可以自己编写所有这些测试,也可以仅使用模拟。说真的,只需使用模拟。

    这个快速示例展示了仅使用 API 的一小部分可以实现的功能。它易于阅读,编写速度非常快:

    MockEndpoint mock = getMockEndpoint("mock:result");
    mock.expectedMessageCount(1);
    mock.expectedBodiesReceived("Hello, world!");
    ​
    // Verify that the right message arrived within 60 seconds
    assertMockEndpointsSatisfied(60, TimeUnit.SECONDS);

    以及如何在不重写实际路线代码的情况下在路线中放置模拟?嗯,有一个答案......

  • 使您的端点可配置- 而不是在您的路由中硬编码端点 URI,请使用占位符。这为您提供了更大的灵活性,能够在测试时用您喜欢的任何东西替换您的端点。这是将模拟端点放入路由的理想方式。

    如果您使用的是 Spring Boot,这很容易。例如,在下面的路由中,我将fromto端点作为占位符:

    from("{{uri.from}}")
        .bean(MyCustomBean.class)
        .to("{{uri.to}}");

    然后,在你的测试类中,你可以用你喜欢的任何端点替换它们:

    @SpringBootTest(classes = YourApplication.class, properties = {
        "uri.from = direct:start",
        "uri.to   = mock:output" })
  • 不要为 Camel 功能编写测试——例如,不要编写一个测试来检查 Camel 是否可以编写文件,或者它是否可以连接到 ActiveMQ。Camel 已经在其源代码中测试了所有这些!

    相反,最好专注于测试您的业务逻辑和代码。例如,您应该测试是否根据客户的忠诚度状态正确标记了客户,或者您的订单验证是否有效。

  • 每当您修复错误或发现边缘情况时,编写额外的测试- 使用错误修复作为编写新测试的机会,以便不会重新引入相同的错误。

    @Test
    public void testCustomerWithNoAddressIsRejected() {
      //...write stuff here
    }

所以你有它。您可以使用模拟端点和断言在 Apache Camel 中测试您的路由。

posted @ 2021-09-26 16:38  yiwenzhang  阅读(1145)  评论(0编辑  收藏  举报