如何在 Apache Camel 中对生产路线进行单元测试?
假设我在单独的 RouteBuilder 类中创建了路线。它看起来像:
- 从 JMS 队列中获取消息,
- 进行一些转换、验证等
- 根据验证结果转发到特定的 JMS 队列,
,并将某些内容保存在数据库中。我想在没有 JMS 代理和数据库的情况下对该路由进行单元测试。我知道我可以模拟我的处理器实现,但这还不够。我不想更改这条路线(假设我在 jar 文件中得到了该类)。据我从《Camel in Action》(第 6.2.6 节)中了解到,为了能够使用端点的模拟和其他内容,我需要更改我的路由端点定义(在本书的示例中,这是“mina:tcp:/”的更改) /米兰达”到“模拟:米兰达”等)。
是否可以在不更改路由定义的情况下完全隔离地测试流? 如果我将 RouteBuilder 作为一个单独的类,我是否被迫以某种方式“复制”路由定义并手动更改它?是不是测试错了?
我对 Camel 很陌生,对我来说,能够在开发路线时进行独立的单元测试真的很酷。只是为了能够改变一些东西,进行小测试,观察结果等等。
Let's say I have my routes created in separate RouteBuilder class. It looks like:
- grab message from JMS queue
- do some transformation, validation etc
- depending on validation results forward to specific JMS queue and save something in DB
I'd like to unit test this route with no JMS broker and no DB. I know I can mock my Processor implementations but that's not enough. I don't want to change this route (let's suppose I got that class in jar file). As far as I know from Camel in Action (sec. 6.2.6), to be able to use mocks of endpoints and other stuff I need to change my route endpoint definitions (in book's example this is change of "mina:tcp://miranda" to "mock:miranda" etc).
Is it possible to test the flow in complete isolation without changing route definitions?
If I got my RouteBuilder as a separate class, am I forced to somehow "copy" route definition and change it manually? Isn't it testing the wrong thing?
I'm quite new to Camel and for me it'd be really cool to be able to have isolated unit test while deveoping routes. Just to be able to change something, run small test, observe result and so on.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
假设 RouteBuilder 类具有硬编码端点,那么测试起来会有点困难。但是,如果 RouteBuilder 使用端点 uri 的属性占位符,那么您通常可以使用一组不同的端点 uri 进行单元测试。正如 Camel 书第 6 章所解释的那样。
如果它们是硬编码的,那么您可以在单元测试中使用带有功能的建议,如下所示: https://camel.apache.org/components/latest/others/test-cdi.html#CDITesting-RoutesadvisingwithadviceWith
在 Camel 2.7 中,我们使操作路线变得更加容易,这样你就可以移除零件,更换零件等。这就是链接谈到的编织内容。
例如,要模拟向数据库端点发送消息,您可以使用上面的方法,并将 to 替换为另一个将其发送到模拟的端点。
在以前的版本中,您可以使用interceptSendToEndpoint技巧,这在Camel书中也有介绍(第6.3.3节)
哦,您也可以用模拟组件替换组件,如第169页所示。现在在Camel 2.8中,模拟组件将不再抱怨它不知道的 uri 参数。这意味着在每个组件级别上用模拟替换组件要容易得多。
Assuming the RouteBuilder class has hardcoded endpoints then its a bit tougher to test. However if the RouteBuilder using the property placeholder for endpoint uris, then you often will be able to use a different set of endpoint uris for unit tests. As explained in chapter 6 of the Camel book.
If they are hardcoded then you can use the advice with feature in your unit test as shown here: https://camel.apache.org/components/latest/others/test-cdi.html#CDITesting-RoutesadvisingwithadviceWith
In Camel 2.7 we made it possible to manipulate the route much easier, so you can remove parts, replace parts, etc. Thats the weaving stuff that link talks about.
For example to simulate sending a message to a database endpoint, you can use that above and replace the to with another where you send it to a mock instead.
In previous releases you can use the interceptSendToEndpoint trick, which is also covered in the Camel book (section 6.3.3)
Oh you can also replace components with mock component as shown on page 169. Now in Camel 2.8 onwards the mock component will no longer complain about uri parameters it doesnt know. That means its much easier to replace components with mocks on a per component level.
我
在我的 spring 文件中,然后在测试类路径上的 shop.properties 中,我有一个 stock.out=xxxx ,它在运行时被替换,所以我可以有不同的路线,一个用于运行时,一个用于测试,
有一个更好的例子6.1.6 多环境下的单元测试
I have
in my spring file and then in the shop.properties on the test class path i have a stock.out=xxxx which is replaced at runtime so i can have to different routes one for runtime and one for test
theres a better example in 6.1.6 unit testing in multiple environments
虽然您可以按照克劳斯·易卜生 (Claus Ibsen) 的说法使用拦截和建议来交换端点
答案,我认为允许您的路线接受
Endpoint
会更好实例,以便您的测试不会耦合到生产端点 URI。
例如,假设您有一个看起来像这样的
RouteBuilder
您可以像这样注入端点:
然后可以像这样进行测试:
这具有以下优点:
CamelTestSupport
或其他辅助类,CamelContext
是手动创建的,因此您可以确保只创建被测试的路由While you can use intercepts and advice to swap out endpoints as per Claus Ibsen's
answer, I think that it is far better to allow your routes to accept
Endpoint
instances so that your tests aren't coupled to your production endpoint URIs.
For example, say you have a
RouteBuilder
that looks something likeYou can make it possible to inject endpoints like so:
Which can then be tested like this:
This has the following advantages:
CamelTestSupport
or other helper classesCamelContext
is created by hand so you can be sure that only the route under test is created如果您使用 Spring(这主要是一个好主意),我想
分享我的方法。
你的生产路线是一个具有特殊类别的 Spring bean
我的路线
<前><代码>@Component
公共类 MyRoute 扩展 RouteBuilder {
公共静态最终字符串IN =“jms://inqueue”;
@覆盖
公共无效配置()抛出异常{
来自(IN)
.process(交换->{
// 通过交换做一些事情
})
.to("activemq:somequeue");
}
}
所以在测试中你可以像这样轻松地覆盖它(这是一个
spring java config内部(到测试类)类):
注意 super.configure() 安装你的生产路线,你可以
使用interceptFrom、interceptSendToEndpoint注入测试代码:eg
引发异常。
我还添加了一些辅助路线。通过这条路线,我可以测试文件是否具有
已在输出文件夹中生成,它可能是 JMS 消费者...
<前><代码>@Bean
公共 RouteBuilder createOutputRoute() {
返回新的 RouteBuilder() {
@覆盖
公共无效配置(){
来自F(FILE_IN,
输出目录)
.to("模拟:输出")
.routeId("完成路由");
};
对于 JMS/JDBC/... 还有 Mockrunner。使用测试配置中的以下代码,您几乎完成了:您的 JMS 连接工厂已被模拟实现替换,因此现在您甚至可以将某些内容放入 JMS 并从 jms 读取(使用如上所述的简单骆驼路由)进行验证。不要忘记在模拟上创建一个队列。
@Bean(JMS_MOCK_CONNECTION_FACTORY)
@基本的
公共 ConnectionFactory jmsConnectionFactory() {
return (new JMSMockObjectFactory()).getMockQueueConnectionFactory();
}
In case you are using Spring (which is mostly a good idea), I'd like
to share my approach.
Your production route is a Spring bean with it's special class
MyRoute
So in the test you can easily override it like this (this is a
spring java config internal (to the test class) class):
Notice super.configure() installs your production route and you may
use interceptFrom, interceptSendToEndpoint to inject test code: e.g.
raise an exception.
I also add some helper routes. With this route I can test, that a file has
been generated in an output folder, it may be a JMS consumer...
For JMS/JDBC/... there is also Mockrunner. With the code below in your test config you are almost done: your JMS Connection Factory is replaced by a mock implementation, so now you can even put something to JMS and read from jms (using simple camel route like explained above) to verify. Don't forget to create a queue on the mock.
@Bean(JMS_MOCK_CONNECTION_FACTORY)
@Primary
public ConnectionFactory jmsConnectionFactory() {
return (new JMSMockObjectFactory()).getMockQueueConnectionFactory();
}
I don't like AdviseWith, yes it's flexible, but it requires you in the test to manually handle camelContext, which is too intrusive for me. I don't want to put that code in hundreds of tests and I also don't want to create a framework around it. Also subclassing CamelTestSupport can be a problem for example if you would use two libraries, which both require you to subclass something and you might have your own test class hierarchy, where you don't see CamelTestSupport. I try not to have class hierarchy in my tests to leave the tests independent. Subclassing means, that you require some magic to happen (you don't directly see that code in the test). If you modify that magic, you would affect a lot of tests. I use spring java config sets for that.