Rom*_*ner 7 java unit-testing apache-camel
我有一个非常简单的Camel路由定义,它只包含一些OnException谓词来处理各自的异常和一些日志语句.
from("hazelcast:seda:someQueue")
.id("someQueueID")
.onException(CustomException.class)
.handled(true)
.log(LoggingLevel.WARN, "custom exception noticed")
.end()
.onException(IOException.class, FileNotFoundException.class)
.asyncDelayedRedelivery()
.redeliveryDelay(3*1000*60) // 3 Minutes
.maximumRedeliveries(3)
.log(LoggingLevel.WARN, "io exception noticed")
.end()
.onException(Exception.class)
.log(LoggingLevel.WARN, "general exception noticed")
.end()
.log("Starting route")
.bean(TestBean.class)
.log("Finished route");
Run Code Online (Sandbox Code Playgroud)
bean本身也很简单,它只是检查一个header参数并抛出一个适当的异常
public class TestBean
{
@Handler
public void checkData(@Headers final Map<String, Object> headers)
throws CustomException, IOException, Exception
{
Integer testVal = (Integer)headers.get("TestValue");
if (0 == testVal)
throw new CustomException("CustomException");
else if (1 == testVal)
throw new IOException("IOException");
else
throw new Exception("Exception");
}
}
Run Code Online (Sandbox Code Playgroud)
由于这个测试设置只是一个较大项目的一小部分,这样做可能听起来很愚蠢,但核心意图是在测试时修改redeliveryDelay,因为"强制"IOException不需要等待3分钟因此,为了加快单位测试,可以将重新传递延迟减少到10毫秒.
为了实现这一点,我的测试方法执行以下操作:
@ContextConfiguration(classes = OnExceptionRouteTest.ContextConfig.class, loader = AnnotationConfigContextLoader.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class OnExceptionRouteTest extends CamelSpringTestSupport
{
@Override
protected AbstractApplicationContext createApplicationContext()
{
return new AnnotationConfigApplicationContext(ContextConfig.class)
}
@Configuration
public static class ContextConfig extends CamelConfiguration
{
@Override
protected void setupCamelContext(CamelContext camelContext) throws Exception
{
super.setupCamelContext(camelContext);
camelContext.addComponent("hazelcast", new StubComponent());
// some other unnecessary stuff
}
@Override
public List<RouteBuilder> routes()
{
final List<RouteBuilder> list = new ArrayList<>();
list.add(new OnExceptionRoute());
return list;
}
}
@Override
public boolean isUseAdviceWith()
{
return true;
}
@Test
public void testIOException()
{
context.getRouteDefinition("someQueueID")
.adviceWith(context, new AdviceWithRouteBuilder()
{
@Override
public void configure() throws Exception
{
this.weaveByType(OnExceptionDefinition.class)
.selectIndex(1)
.replace()
.onException(IOException.class, FileNotFound.class)
.asyncDelayedRedelivery()
.redeliveryDelay(10)
.maximumRedeliveries(3)
.log("modified io exception noticed")
.to("mock:ioError")
.end();
...
mockEndpoints();
}
});
context.start();
MockEndpoint ioErrorEndpoint = getMockEndpoint("mock:ioError");
...
ioErrorEndpoint.setExpectedMessageCount(1);
...
Map<String, Object> headers = new HashMap<>();
headers.put("TestValue", new Integer(1));
template.sendBodyAndHeaders("hazelcast:seda:someQueue", new Object(), headers);
...
ioErrorEndpoint.assertIsSatisfied();
...
}
}
Run Code Online (Sandbox Code Playgroud)
这里测试只是替换IOException的onException段,首先将重新传递延迟从3分钟减少到10毫秒,并在最后添加一个模拟端点.但是,当我尝试运行单元测试时,我将得到以下异常:
java.lang.IllegalArgumentException: The output must be added as top-level on the route. Try moving OnException[[class java.io.IOException, class java.io.FileNotFoundException] -> []] to the top of route.
Run Code Online (Sandbox Code Playgroud)
但是,正如我所理解的那样,官方文档中的示例非常相似.我还尝试通过定义的ID谓词及其相应的方法weaveById()或通过weaveByToString()方法查找异常定义,但没有其他结果.我还尝试通过weaveByType(OnExceptionDefinition.class).selectIndex(1).remove();并删除异常定义,并添加OnException部分,weaveAddFirst().onException(...).async...;但结果相同.
但是,通过fe可以附加模拟的错误端点 weaveByToString("Log[io exception noticed]").after().to("mock:ioError");
因此,任何有关修改onException块或redeliveryDelay以进行单元测试的提示都非常受欢迎.
@Edit:我现在也尝试from(...)按照异常消息的建议将onException声明移到路由定义()之上,这也是Camel 异常样本中的首选情况.但是,在执行此操作时,所有测试(甚至是工作测试)都会失败NullPointerException,context.getRouteDefinition("someQueueID").adviceWith(context, new AdviceWithRouteBuilder() {... });因为显然路径本身无法再找到.我怀疑这是一个IntelliJ问题,因为两个类都在同一个项目中,因此对于测试类应该可以看到路径的修改.
使用的Camel版本:2.13.0,IntelliJ IDEA 13.1.2
@ Edit2:由于某些原因,context.getRouteDefinitions("someQueueID")如果OnException元素是在from块之外定义的,则返回null ,而通用路由可以通过context.getRouteDefinitions().get(0)- 但是,表示需要将OnException部分添加为顶级元素的异常保留.
使用Java DSL时,使用该.routeId()方法设置路由的ID ,而不是.id()上面编码的.这可能有助于解决您的adviceWith问题.
而不是硬编码重试延迟,更好的方法是使用属性使延迟可配置.查看useOverridePropertiesWithPropertiesComponent()有关您CamelSpringTestSupport班级方法的文档.
编辑
您不必编织该onException条款,只需说明一个新条款即可.这是一个完整的例子:
import org.apache.camel.builder.AdviceWithRouteBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.test.junit4.CamelTestSupport;
public class DummyTest extends CamelTestSupport{
@Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder(){
@Override
public void configure() throws Exception {
from("direct://start")
.routeId("myroute")
.onException(Exception.class)
.id("myException")
.continued(true)
.end()
.throwException(new Exception())
.to("mock:end");
}
};
}
@org.junit.Test
public void doTest() throws Exception{
context.getRouteDefinition("myroute").adviceWith(context, new AdviceWithRouteBuilder(){
@Override
public void configure() throws Exception {
context.getRouteDefinition("myroute")
.onException(Exception.class).setBody(constant("adviceWith")).continued(true);
}});
context.start();
template.sendBody("direct://start", "original");
String bodyAtEndOfExchange = getMockEndpoint("mock:end")
.getExchanges().get(0).getIn().getBody(String.class);
assertEquals("adviceWith", bodyAtEndOfExchange);
context.stop();
}
@Override
public boolean isUseAdviceWith() {
return true;
}
}
Run Code Online (Sandbox Code Playgroud)