第 6 章 测试


6.1. 测试 Camel Quarkus 扩展

测试提供了确保 Camel 路由在一段时间内的行为良好的方法。如果您还没有这样做,请阅读 Camel Quarkus 用户指南第一步和 Quarkus 文档 测试应用程序 部分。https://camel.apache.org/camel-quarkus/3.20.x/user-guide/first-steps.html

在 Quarkus 上下文中测试路由时,建议的做法是编写本地集成测试。这具有涵盖 JVM 和原生模式的优点。

在 JVM 模式中,您可以使用 CamelTestSupport 风格测试

6.1.1. 在 JVM 模式下运行

在 JVM 模式中,使用 @QuarkusTest 注释来引导 Quarkus,并在 @Test 逻辑执行前启动 Camel 路由。

例如:

import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;

@QuarkusTest
class MyTest {
    @Test
    public void test() {
        // Use any suitable code that sends test data to the route and then assert outcomes
        ...
    }
}
Copy to Clipboard Toggle word wrap
提示

您可以在 Camel Quarkus 源中找到示例实现:

6.1.2. 在原生模式下运行

注意

始终测试您的应用程序是否在原生模式中适用于所有支持的扩展。

您可以通过从相应的 JVM 模式类继承逻辑来重复利用为 JVM 模式定义的测试逻辑。

添加 @QuarkusIntegrationTest 注释,以指示 Quarkus JUnit 扩展编译应用程序到原生镜像,并在运行测试前启动它。

import io.quarkus.test.junit.QuarkusIntegrationTest;

@QuarkusIntegrationTest
class MyIT extends MyTest {
   ...
}
Copy to Clipboard Toggle word wrap
提示

您可以在 Camel Quarkus 源中找到示例实现:

原生可执行文件不需要 JVM 来运行,且不能在 JVM 中运行,因为它是原生代码,而不是字节代码。

对原生代码进行编译测试没有点,以便它们使用传统的 JVM 运行。

这意味着测试和应用程序之间的通信必须通过网络(HTTP/REST 或其他协议)通过监视文件系统(例如,日志文件)或其他进程间通信。

6.1.3.1. JVM 模式的 @QuarkusTest

在 JVM 模式中,使用 @QuarkusTest 标注的测试在与测试中的应用相同的 JVM 中执行。

这意味着,您可以使用 @Inject 将来自应用的 Bean 添加到测试代码中。

您还可以使用 @jakarta. enterprise.inject.Alternative 和 @jakarta.annotation.Priority 来定义新的 Bean,甚至覆盖来自应用程序的 Bean。

6.1.3.2. 在原生模式中 @QuarkusIntegrationTest

在原生模式中,在独立于正在运行的原生应用程序的 JVM 中,使用 @QuarkusIntegrationTest 标注的测试。

这一点非常重要,即测试与原生应用程序之间的所有通信都必须采用以下一个或多个形式:

  • 网络调用。通常,应用程序支持的 HTTP 或任何其他网络协议。
  • 监视文件系统的更改。(例如,通过 Camel 文件 端点。)
  • 任何其他类型的进程间通信。

QuarkusIntegrationTest 提供了通过 @QuarkusTest 不提供的额外功能:

  • 在 JVM 模式中,您可以启动并测试 Quarkus 构建生成的可运行应用程序 JAR。
  • 在原生模式中,您可以启动并测试 Quarkus 构建生成的原生应用程序。
  • 如果您在构建中添加容器镜像,则容器将启动并测试。

有关 QuarkusIntegrationTest 的更多信息,请参阅 Quarkus 测试指南

6.1.4. 使用外部服务进行测试

6.1.4.1. Testcontainers

有时,应用程序需要访问一些外部资源,如消息传递代理、数据库或其他服务。

如果容器镜像可用于感兴趣的服务,您可以使用 Testcontainers 在测试过程中启动并配置服务。

要使应用正常工作,在启动前,通常要将连接配置数据(主机、端口、用户、远程服务密码)传递给应用程序。

在 Quarkus 生态系统中,QuarkusTestResourceLifecycleManager 提供此目的。

您可以使用 start () 方法启动一个或多个 Testcontainers,并以 Map 的形式从方法返回连接配置。

然后,这个映射的条目会根据模式以不同的方式传递给应用程序:

  • 原生模式:命令行(-Dkey=value)
  • JVM 模式:特殊的 MicroProfile 配置提供程序
注意

命令行和 MicroProfile 设置的优先级高于 application.properties 文件中的设置。

import java.util.Map;
import java.util.HashMap;

import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;

public class MyTestResource implements QuarkusTestResourceLifecycleManager {

    private GenericContainer<?> myContainer;

    @Override
    public Map<String, String> start() {
        // Start the needed container(s)
        myContainer = new GenericContainer(DockerImageName.parse("my/image:1.0.0"))
                .withExposedPorts(1234)
                .waitingFor(Wait.forListeningPort());

        myContainer.start();

        // Pass the configuration to the application under test
        // You can also pass camel component property names / values to automatically configure Camel components
        return new HashMap<>() {{
                put("my-container.host", container.getHost());
                put("my-container.port", "" + container.getMappedPort(1234));
        }};
    }

    @Override
    public void stop() {
        // Stop the needed container(s)
        myContainer.stop();
        ...
    }
}
Copy to Clipboard Toggle word wrap

使用 @QuarkusTestResource 从测试类引用定义的测试资源:

import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
@QuarkusTestResource(MyTestResource.class)
class MyTest {
   ...
}
Copy to Clipboard Toggle word wrap
提示

您可以在 Camel Quarkus 源中找到示例实现:

6.1.4.2. WireMock

例如,如果测试不可用、不可靠或昂贵的,您可以 stub 与第三方服务和 API 交互,而不是将测试连接到实时端点。

您可以使用 WireMock 进行模拟和记录 HTTP 交互。它在整个 Camel Quarkus 测试套件中广泛使用,用于各种组件扩展。

6.1.4.2.1. 设置 WireMock

流程

  1. 设置 WireMock 服务器。

    注意

    始终在 test 下配置 Camel 组件,以通过 WireMock 代理传递任何 HTTP 交互。您可以通过配置决定 API 端点 URL 的组件属性来实现此目的。

    import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
    import static com.github.tomakehurst.wiremock.client.WireMock.get;
    import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
    import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import com.github.tomakehurst.wiremock.WireMockServer;
    
    import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
    
    public class WireMockTestResource implements QuarkusTestResourceLifecycleManager {
    
        private WireMockServer server;
    
        @Override
        public Map<String, String> start() {
            // Setup & start the server
            server = new WireMockServer(
                wireMockConfig().dynamicPort()
            );
            server.start();
    
            // Stub an HTTP endpoint. WireMock also supports a record and playback mode
            // https://wiremock.org/docs/record-playback/
            server.stubFor(
                get(urlEqualTo("/api/greeting"))
                    .willReturn(aResponse()
                        .withHeader("Content-Type", "application/json")
                        .withBody("{\"message\": \"Hello World\"}")));
    
            // Ensure the camel component API client passes requests through the WireMock proxy
            Map<String, String> conf = new HashMap<>();
            conf.put("camel.component.foo.server-url", server.baseUrl());
            return conf;
        }
    
        @Override
        public void stop() {
            if (server != null) {
                server.stop();
            }
        }
    }
    Copy to Clipboard Toggle word wrap
  2. 确保您的测试类具有 @QuarkusTestResource 注释,并将适当的 test 资源类指定为值。WireMock 服务器将在执行所有测试之前启动,并在所有测试完成后关闭。
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
@QuarkusTestResource(WireMockTestResource.class)
class MyTest {
   ...
}
Copy to Clipboard Toggle word wrap

WireMock 服务器在所有测试执行前启动,并在所有测试完成后关闭。

提示

您可以在 Camel Quarkus 集成测试源树中找到示例实现:

从 Camel Quarkus 2.13.0 开始,您可以使用 CamelQuarkusTestSupport 进行测试。它是 CamelTestSupport 的替代品,其不适用于 Quarkus。

重要

CamelQuarkusTestSupport 仅适用于 JVM 模式。如果您需要以原生模式进行测试,请使用上述其中一个替代测试策略。

将以下依赖项添加到模块中(最好在 测试 范围内):

<dependency>
    <groupId>org.apache.camel.quarkus</groupId>
    <artifactId>camel-quarkus-junit5</artifactId>
    <scope>test</scope>
</dependency>
Copy to Clipboard Toggle word wrap

您可以在测试中使用 CamelQuarkusTestSupport,如下所示:

@QuarkusTest
@TestProfile(SimpleTest.class) //necessary only if "newly created" context is required for the test (worse performance)
public class SimpleTest extends CamelQuarkusTestSupport {
    ...
}
Copy to Clipboard Toggle word wrap

6.1.5.2. 自定义 CamelContext 进行测试

您可以使用 配置文件、CDI Bean、观察程序、模拟 等自定义 CamelContext 进行测试。您还可以覆盖 createCamelContext 方法,并直接与 CamelContext 交互。

重要

使用 createCamelContext 时,您必须实例化并返回新的 CamelContext相反,调用 super.createCamelContext (),并根据需要修改返回的 CamelContext。无法遵循此规则将导致抛出异常。

@QuarkusTest
class SimpleTest extends CamelQuarkusTestSupport {

    @Override
    protected CamelContext createCamelContext() throws Exception {
        // Must call super to get a handle on the application scoped CamelContext
        CamelContext context = super.createCamelContext();
        // Apply customizations
        context.setTracing(true);
        // Return the modified CamelContext
        return context;
    }
}
Copy to Clipboard Toggle word wrap

6.1.5.3. 配置路由用于测试

在您的应用程序中扩展 RouteBuilder 的任何类都会将其路由自动添加到 CamelContext 中。同样,从 camel.main.routes-include-pattern 配置的任何 XML 或 YAML 路由也会被加载。

这可能并不适用于您的测试。您可以使用配置属性控制在测试时载入哪些路由:

  • quarkus.camel.routes-discovery.include-patterns
  • quarkus.camel.routes-discovery.exclude-patterns,
  • camel.main.routes-include-pattern
  • camel.main.routes-exclude-pattern.

您还可以通过覆盖 createRouteBuilder 来定义每个测试类的测试特定路由:

@QuarkusTest
class SimpleTest extends CamelQuarkusTestSupport {
    @Test
    void testGreeting() {
        MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
        mockEndpoint.expectedBodiesReceived("Hello World");

        template.sendBody("direct:start", "World");

        mockEndpoint.assertIsSatisified();
    }

    @Override
    protected RoutesBuilder createRouteBuilder() throws Exception {
        return new RouteBuilder() {
            @Override
            public void configure() throws Exception {
                from("direct:start")
                    .transform().simple("Hello ${body}")
                    .to("mock:result");
            }
        };
    }
}
Copy to Clipboard Toggle word wrap

6.1.5.4. CamelContext 测试生命周期

CamelTestSupport 相比,CamelQuarkusTestSupport 的主要区别之一是如何管理 CamelContext 生命周期。

在 Camel Quarkus 上,运行时会自动为您创建一个 CamelContext。默认情况下,此 CamelContext 在所有测试之间共享,并在整个测试套件执行期间保持启动。

这可能会对您的测试造成一些意外的副作用。如果您需要在测试之间重新启动 CamelContext,则可以创建自定义 测试配置文件,这将强制应用程序在测试后重新启动。

例如,定义测试配置集:

@QuarkusTest
class MyTestProfile implements QuarkusTestProfile {
    ...
}
Copy to Clipboard Toggle word wrap

然后,使用 @TestProfile 在测试类中引用它:

// @TestProfile will trigger the application to be restarted
@TestProfile(MyTestProfile.class)
@QuarkusTest
class SimpleTest extends CamelQuarkusTestSupport {
    ...
}
Copy to Clipboard Toggle word wrap
注意

您无法通过调用其 stop ()start () 方法来手动重启 CamelContext。这将会产生一个例外。

6.1.5.5. 例子

6.1.5.5.1. 简单 RouteBuilder 和 test 类

simple RouteBuilder

public class MyRoutes extends RouteBuilder {
    @Override
    public void configure() {
        from("direct:start")
            .transform().simple("Hello ${body}")
            .to("mock:result");
    }
}
Copy to Clipboard Toggle word wrap

测试向 direct:start 端点发送消息有效负载:

@QuarkusTest
class SimpleTest extends CamelQuarkusTestSupport {
    @Test
    void testGreeting() {
        MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
        mockEndpoint.expectedBodiesReceived("Hello World");

        template.sendBody("direct:start", "World");

        mockEndpoint.assertIsSatisified();
    }
}
Copy to Clipboard Toggle word wrap
6.1.5.5.2. 使用 AdviceWith
@QuarkusTest
class SimpleTest extends CamelQuarkusTestSupport {
    @BeforeEach
    public void beforeEach() throws Exception {
        AdviceWith.adviceWith(this.context, "advisedRoute", route -> {
            route.replaceFromWith("direct:replaced");
        });
    }

    @Override
    protected RoutesBuilder createRouteBuilder() throws Exception {
        return new RouteBuilder() {
            @Override
            public void configure() throws Exception {
                from("direct:start").routeId("advisedRoute")
                    .transform().simple("Hello ${body}")
                    .to("mock:result");
            }
        };
    }

    @Test
    void testAdvisedRoute() throws Exception {
        MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
        mockEndpoint.expectedBodiesReceived("Hello World");

        template.sendBody("direct:replaced", "World");

        mockEndpoint.assertIsSatisfied();
    }
}
Copy to Clipboard Toggle word wrap
6.1.5.5.3. 显式启用建议

当显式启用 建议 时,您必须在完成 AdviceWith 设置时调用 startRouteDefinitions

注意

只有在您配置了未建议的路由时,才需要调用 startRouteDefinitions

6.1.5.6. 限制:

CamelQuarkusTestSupport 继承 CamelTestSupport 的一些测试生命周期方法。但是,不应使用它们,而是替换为 CamelQuarkusTestSupport 中的等效方法。

Expand
CamelTestSupport 生命周期方法CamelQuarkusTestSupport 等

afterAll

doAfterAll

afterEach, afterTestExecution

doAfterEach

beforeAll

doAfterConstruct

beforeEach

doBeforeEach

6.1.5.6.2. 不支持创建自定义 Camel registry

CamelQuarkusTestSupportcreateCamelRegistry 实施会抛出 UnsupportedOperationException

如果需要将对象绑定到 Camel registry,则您可以使用以下方法之一完成此操作。

  • 生成命名的 CDI Bean

    public class MyBeanProducers {
        @Produces
        @Named("myBean")
        public MyBean createMyBean() {
            return new MyBean();
        }
    }
    Copy to Clipboard Toggle word wrap
  • override createCamelContext (请参阅上面的示例)并调用 camelContext.getRegistry ().bind ("foo", fooBean)
  • 使用 @BindToRegistry 注释

    @QuarkusTest
    class SimpleTest extends CamelQuarkusTestSupport {
        @BindToRegistry("myBean")
        MyBean myBean = new MyBean();
    }
    Copy to Clipboard Toggle word wrap
    注意

    Bean 从单个测试类绑定到 Camel registry,将在测试套件执行期间保留。根据您的测试预期,这可能会产生意外的后果。您可以使用测试配置集重启 CamelContext 以避免这种情况。

Red Hat logoGithubredditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。 了解我们当前的更新.

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

Theme

© 2026 Red Hat
返回顶部