测试

NotifyBuilder

师成师成· 更新于 2026-09-28· 阅读 17 分钟· 0 次阅读

登录后可跨设备保存划线和私人笔记登录

NotifyBuilder

NotifyBuilder 是 org.apache.camel.builder 包中的一个构建器,它允许你构建表达式,然后测试或等待某个条件的发生。这些表达式围绕着 Exchange 被路由的通知构建。这是什么意思呢?

它的意思是,你可以构建一个表达式,用来告诉你 Camel 何时完成了对 5 条消息的路由等操作。当你测试一条既不能也不打算使用 Mock 组件的路由时,可能会用到它。

NotifyBuilder 仅用于测试目的,且只能通过 Java 代码使用。

使用 NotifyBuilder 的基本示例

假设我们有一条非常简单的路由:

仅限 Java:用于演示 NotifyBuilder 的简单路由

from("jms:queue:quotes")
    .to("bean:quotes");

想象一下这条路由更复杂,是一条生产就绪的路由。

现在你想要测试这条路由,而不使用 mock 或类似的工具。我们想要测试它是否能够处理发送到该队列的消息。通过使用 NotifyBuilder,我们可以构建一个表达式,用来表示该条件何时发生。

仅限 Java:基本的 NotifyBuilder 等待一条消息完成

NotifyBuilder notify = new NotifyBuilder().whenDone(1).create();

// now use some API to send a message etc. Maybe you cannot use Camel's ProducerTemplate
// now we want to wait until the message has been routed and completed

boolean done = notify.matches(10, TimeUnit.SECONDS);
assertTrue("Should be done", done);

// now maybe use some API to see that the message did as expected

这是一个非常基础的示例,我们在其中配置了 NotifyBuilder,使其在任意 Exchange 完成时进行匹配。

该构建器还提供了许多其他方法,可以设置更复杂的表达式,甚至可以使用 and、or、not 运算进行叠加组合。

NotifyBuilder API

NotifyBuilder 提供了许多方法,可以用来构建更加复杂的表达式。下表列出了最常用的方法。

方法描述
from(endpointUri)仅当 Exchange 从该特定端点进入时匹配。endpointUri 可以是一个模式,其模式匹配方式与 Intercept 所使用的相同。
fromRoute(routeId)仅当 Exchange 从该特定路由进入时匹配。routeId 可以是一个模式,其模式匹配方式与 Intercept 所使用的相同。
filter(predicate)过滤掉不需要的 Exchange(仅使用通过(返回 true)该谓词的消息)。
wereSentTo(endpointUri)仅当 Exchange 在某一时刻曾被发送到给定端点时匹配。endpointUri 可以是一个模式,其模式匹配方式与 Intercept 所使用的相同。
whenDone(number)当有 X 条或更多消息完成时匹配。
whenComplete(number)当有 X 条或更多消息完成(complete)时匹配。
whenFailed(number)当有 X 条或更多消息失败时匹配。
whenBodiesDone(bodies)当消息体按相同顺序完成时匹配。此方法是非严格的,这意味着它会忽略任何额外的已完成消息。
whenAnyDoneMatches(predicate)若任一已完成消息匹配该谓词则匹配。
create创建通知器。创建之后即可使用 matches 相关方法。
matches测试通知器当前是否匹配。此操作会立即返回。该方法应在创建表达式之后使用。
matches(timeout)等待直到通知器匹配或超时。若匹配则返回 true,若发生超时则返回 false。此操作会立即返回。该方法应在创建表达式之后使用。
matchesWaitTime等待直到构建器匹配或超时。所使用的超时值基于所使用的任一 mock 端点上配置的最大结果等待时间。如果没有使用 mock 端点,则默认超时值为 10 秒。在单元测试中使用 mock 时,此方法非常方便,无需显式指定超时值。

NotifyBuilder 拥有超过 30 个方法。如需查看完整的方法列表,请参阅 NotifyBuilder 的 javadoc。

如果你想针对不同的条件接收通知,可以创建多个 NotifyBuilder 实例。NotifyBuilder 还支持使用二元运算符(and、or、not)将多个条件叠加组合。

done、completed 和 failed 的区别

NotifyBuilder 定义了消息完成的三种方式:

  • Done — 消息处理完成,无论它是成功完成还是失败。
  • Completed — 消息成功完成(没有失败)。
  • Failed — 消息失败(例如抛出了异常且未被处理)。

这三种方式的名称也体现在了构建器方法的命名中:whenDone、whenCompleted 和 whenFailed。

示例

纯 Java 方式:当来自特定端点的 5 条消息全部完成时进行匹配

NotifyBuilder notify = new NotifyBuilder(context)
    .from("direct:foo").whenDone(5)
    .create();

这里我们想要匹配 direct:foo 端点已完成 5 条消息的时刻。

你可能还希望在某条消息完成时根据索引收到通知,例如第一条消息。为此,只需这样做即可:

纯 Java 实现:使用索引匹配第一条消息完成的情况

NotifyBuilder notify = new NotifyBuilder(context)
    .whenDoneByIndex(0)
    .create();

这可以确保通知器仅在第一条消息处理完成时才会匹配。

如果改用 whenDone(1),那么只要有至少一条消息完成,通知器就会匹配。在这种情况下,即使第一条消息尚未完成,whenDone(1) 也可能匹配,因为夹在中间的其他消息可能先于第一条消息完成。这就是引入 whenDoneByIndex 来支持该场景的原因。

这里我们希望在 direct:foo 端点完成 5 条消息、且这些消息的正文中包含 "test" 一词时进行匹配。该过滤器接受一个 Predicate。

仅 Java:在匹配之前使用谓词过滤消息

NotifyBuilder notify = new NotifyBuilder(context)
    .from("direct:foo").filter(body().contains("test")).whenDone(5)
    .create();

在这里我们只需要说明,从任意 JMS 端点至少接收到一条消息即可(注意通配符匹配)。

仅 Java 实现:使用通配符匹配来自任意 JMS 端点的消息

NotifyBuilder notify = new NotifyBuilder(context)
    .from("jms:*").whenDone(1)
    .create();

在这里,我们只是说明:至少有三条消息已从 myCoolRoutes 中的任意路由接收完毕(注意通配符匹配)。

仅限 Java:使用通配符匹配来自路由的消息

NotifyBuilder notify = new NotifyBuilder(context)
.fromRoute("myCoolRoutes*").whenDone(3)
.create();

这里 5 条 foo 消息和 7 条 bar 消息都必须完成。注意 and 运算符的使用。

仅限 Java:使用 and 运算符合并条件

NotifyBuilder notify = new NotifyBuilder(context)
    .from("direct:foo").whenDone(5)
    .and().from("direct:bar").whenDone(7)
    .create();

这里我们期望接收两条消息,其消息体分别是 Hello World 和 Bye World。

仅 Java:匹配期望的消息体

NotifyBuilder notify = new NotifyBuilder(context)
    .from("direct:foo").whenBodiesReceived("Hello World", "Bye World")
    .create();

这里我们期望接收到一条正文中包含 Camel 的消息。

仅限 Java:使用谓词匹配任意接收到的消息

NotifyBuilder notify = new NotifyBuilder(context)
    .whenAnyReceivedMatches(body().contains("Camel"))
    .create();

使用 Mock 端点进行细粒度断言

仅限 Java:将 Mock 端点与 NotifyBuilder 结合使用

// let's use a mock to set the expressions as it got many great assertions for that
// notice we use mock:assert which does NOT exist in the route, it's just a pseudo name
MockEndpoint mock = getMockEndpoint("mock:assert");
mock.expectedBodiesReceivedInAnyOrder("Hello World", "Bye World", "Hi World");

NotifyBuilder notify = new NotifyBuilder(context)
    .from("direct:foo").whenReceivedSatisfied(mock)
    .create();

这里我们将 Mock 与 NotifyBuilder 结合使用。我们使用 mock 来设置细粒度的期望,例如按任意顺序接收到 3 条消息。然后使用构建器可以指明这些消息应当来自 direct:foo 端点。你可以根据需要组合多个表达式。不过,我们建议对于你可能已经熟悉的细粒度期望使用 mock。你还可以指定 Exchange 必须已被发送到某个给定端点。

使用 wereSentTo

例如,在下面的例子中,我们期望消息被发送到 mock:bar

仅限 Java:匹配那些被发送到特定端点的消息

NotifyBuilder notify = new NotifyBuilder(context)
    .wereSentTo("mock:bar")
    .create();

你可以将此与其他任意期望条件组合使用,例如:仅当有 3 条或更多消息处理完成且被发送到 mock:bar 端点时才匹配。

仅使用 Java:将 whenDone 与 wereSentTo 组合

NotifyBuilder notify = new NotifyBuilder(context)
    .whenDone(3).wereSentTo("mock:bar")
    .create();

你可以添加额外的 wereSentTo 条件,例如下面这两个:

仅限 Java:链式组合多个 wereSentTo 条件

NotifyBuilder notify = new NotifyBuilder(context)
    .wereSentTo("activemq:queue:foo").wereSentTo("activemq:queue:bar")
.create();

同样,你还可以预期有若干消息处理完成,以及一条消息处理失败并需要被发送到另一个端点:

仅限 Java:在不同端点上组合 done 与 failed 条件

NotifyBuilder notify = new NotifyBuilder(context)
    .whenDone(3).wereSentTo("activemq:queue:goodOrder")
    .and().whenFailed(1).wereSentTo("activemq:queue:badOrder")
    .create();

评论

登录后参与评论

正在加载评论…