Java 如何在骆驼测试中模拟AMQP消费者?
假设我有以下路线:Java 如何在骆驼测试中模拟AMQP消费者?,java,testing,mocking,apache-camel,Java,Testing,Mocking,Apache Camel,假设我有以下路线: from(rabbitMQUri) .to(myCustomerProcessor) .choice() .when(shouldGotoA) .to(fizz) .when(shouldGotoB) .to(buzz) .otherwise() .to(foo); 让我们假设myCustomProcessor根据从RabbitMQ消耗的消
from(rabbitMQUri)
.to(myCustomerProcessor)
.choice()
.when(shouldGotoA)
.to(fizz)
.when(shouldGotoB)
.to(buzz)
.otherwise()
.to(foo);
让我们假设myCustomProcessor
根据从RabbitMQ消耗的消息来调谐shouldGotoA
和shouldGotoB
我想对3个场景进行单元测试:
shouldGotoA
被设置为true,它在(…)时执行第一条
shouldGotoB
被设置为true,当(…)时执行第二个
否则()
一个代码示例或代码片段将非常有用,非常感谢 这可能取决于您使用的通信组件(AMQP或RabbitMQ) Camel中示例代码最重要的资源是源代码中的junit测试用例 这两个文件的功能与您所需要的类似,但您可能希望在测试用例中寻找灵感: 使路由可测试的更“基本”的方法是将“from”uri作为参数。 假设您将RouteBuilder制作为如下内容:
private String fromURI = "amqp:/..";
public void setFromURI(String fromURI){
this.fromURI = fromURI;
}
public void configure(){
from(fromURI).whatever();
}
@RunWith(JukitoRunner.class)
public class OcsFtpTest extends CamelTestSupport {
public static class TestModule extends JukitoModule {
@Override
protected void configureTest() {
bind(CamelContext.class).to(DefaultCamelContext.class).in(TestSingleton.class);
}
@Provides
@Named("FileEndpoint")
private Endpoint testEndpoint() {
DirectEndpoint fileEndpoint = getContext().getEndpoint("direct:a", DirectEndpoint.class);
return fileEndpoint;
}
}
@Inject
private MyRoutes testObject;
@Test
....
}
然后,在开始单元测试之前,可以在fromURI中注入一个“seda:foobar”端点。终点是。这假设您不需要测试特定于AMQP/RabbitMQ的构造,只需接收有效负载。这是一种组合合适测试的方法 首先定义一个空的Camel上下文,其中只有一个ProducerTemplate:
<camel:camelContext id="camelContext">
<camel:template id="producerTemplate" />
</camel:camelContext>
在测试本身中,将上下文中的RabbitMQ/ActiveMQ/JMS组件替换为seda或direct组件。例如,用seda队列替换所有JMS调用
camelContext.removeComponent("jms");
camelContext.addComponent("jms", this.camelContext.getComponent("seda"));
camelContext.addRoutes(this.documentBatchRouting);
现在,无论何时读取或写入JMS URI,它实际上都将进入seda队列。这类似于向组件中注入一个新的URI,但需要更少的配置,并允许您向路由添加新的端点,而无需担心记住注入所有URI
最后,在测试中,使用生产者模板发送测试消息:
producerTemplate.sendBody("jms:MyQueue", 2);
然后,您需要执行该路线,您可以测试您的期望
需要注意的两件事是:
使软件更好地可测试的一个好方法是使用依赖注入(dependency injection)(尤其是与外部组件通信的软件)。我爱它,它就是。 (所有这些东西都会给你带来学习依赖注入的负担,但我可以向你保证,很快就会有回报的) 在这个场景中,您只需注入“端点”。您可以像这样预先配置端点(将放置在“模块”中) RouteBuilder只需注入端点:
@Inject
private MyRoutes(@Named("FileEndpoint") final Endpoint fileEndpoint) {
this.fileEndpoint = fileEndpoint;
}
@Override
public void configure() throws Exception {
from(fileEndpoint)....
}
为了方便地测试这样的路由,您为测试注入了另一个端点,不是FileEndpoint,而是“direct:something”。一个非常简单的方法是,它将Guice与。测试如下所示:
private String fromURI = "amqp:/..";
public void setFromURI(String fromURI){
this.fromURI = fromURI;
}
public void configure(){
from(fromURI).whatever();
}
@RunWith(JukitoRunner.class)
public class OcsFtpTest extends CamelTestSupport {
public static class TestModule extends JukitoModule {
@Override
protected void configureTest() {
bind(CamelContext.class).to(DefaultCamelContext.class).in(TestSingleton.class);
}
@Provides
@Named("FileEndpoint")
private Endpoint testEndpoint() {
DirectEndpoint fileEndpoint = getContext().getEndpoint("direct:a", DirectEndpoint.class);
return fileEndpoint;
}
}
@Inject
private MyRoutes testObject;
@Test
....
}
现在,“testObject”将获得直接端点,而不是文件端点。这适用于所有类型的端点,通常适用于所有严重依赖接口的接口/抽象类和API(camel在这里很出色!)。谢谢@Petter(+1),但我担心有人会给我一个“你应该注入字符串URI”-键入答案。。。我需要(…)中模拟/存根的内容来实际生成正确路由的消息。我不相信给它注射seda:foo能帮我做到这一点……有什么想法吗?再次感谢!隐马尔可夫模型。。uri注入是我的第二个选择(有时已经足够好了)。查看来自AMQ源代码的两个链接的.java文件,以获得更合适的模拟,以便实际生成消息,只需在测试代码()中使用一个ProducerTemplate即可。还要记住,如果用SEDA或direct endpoint替换RibbitMQ,您的事务行为可能会改变。谢谢@matthelliwell(+1)-但请考虑一下:如果我添加一个
ProducerTemplate
,我应该如何注入它而不是RabbitMQ端点?在我的回答中,有AMQP路由的示例测试类,以及骆驼本身链接到的RabbitMQ路由。他们会用“from”等来测试完整的路线。请阅读。谢谢@matt helliwell(+1)-我今晚回家后会检查这个,并会在这里发帖反馈!同时还有一个快速跟进的问题:当你说我的“交易边界可能会改变”时,你到底是什么意思?你能举个具体的例子吗?请记住,我使用AMQP/RabbitMQ进行实际的消息传递(在生产路线中)。再次感谢!一种情况是,您正在测试的类中进行DB更新,并且依赖测试用例回滚事务。如果您有一个seda/jms队列,那么在测试用例中创建的事务将不会传播到被测试的类,因此您不能回滚它们所做的任何DB更新。如果您使用的是直接组件,它就可以正常工作。类似地,如果在路由中用“direct:”组件替换jms队列,则会得到任何通过“direct:”组件传播的事务。什么是camelContext.addRoutes(this.documentBatchRouting);?我用rabbitmq尝试了这个方法,但组件没有被删除。添加路由后,它将尝试连接到rabbitmq而不是seda。它似乎只适用于可以接受相同参数的端点。SEDA不接受应用于rabbit的所有参数,所以在替换它时失败。我看不到模块类中可用的getContext()函数。你能帮我吗