spring 使用多个上下文的骆驼测试

spring camel test using multiple contexts

我有两个 camel 上下文(A 和 B)在不同的 xml 使用 Spring。当我只加载一个上下文时,我的 junit 工作,但是当我尝试加载两个上下文时,端点注入失败 运行 junit.

所以,有人有一个示例如何使用 spring camel 的多上下文测试?

Spring 测试

public class BaseSpringTest extends CamelSpringTestSupport
{
    protected AbstractXmlApplicationContext createApplicationContext()
    {
        return new ClassPathXmlApplicationContext("camel-config.xml");
    }
} 

我的档案camel-config.xml

<beans>
 <context:annotation-config/>
 <import resource="classpath:camel-test-dao.xml" />
 <import resource="classpath:camel-contextA.xml"/>
 <import resource="classpath:camel-contextB.xml"/>
</beans>

我的背景:

<camelContext xmlns="camel.apache.org/schema/spring" id="contextA"> 
 ...
</camelContext>
<camelContext xmlns="camel.apache.org/schema/spring" id="contextB"> 
...
</camelContext>

我的单元测试,注入失败 Endpoint:

@EndpointInject(uri = "direct:myroute", context="contextB")
private Endpoint eFooTest;

堆栈跟踪:

org.apache.camel.spring.GenericBeansException: Error post processing bean: com.mycompany.test.FooTest; nested exception is java.lang.NullPointerException
  at org.apache.camel.spring.CamelBeanPostProcessor.postProcessBeforeInitialization(CamelBeanPostProcessor.java:154)
  at org.apache.camel.test.spring.CamelSpringTestSupport.postProcessTest(CamelSpringTestSupport.java:62)
  at org.apache.camel.test.junit4.CamelTestSupport.doSetUp(CamelTestSupport.java:319)
  at org.apache.camel.test.junit4.CamelTestSupport.setUp(CamelTestSupport.java:238)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:606)
  at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:47)
  at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
  at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
  at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
  at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
  at org.junit.rules.TestWatcher.evaluate(TestWatcher.java:55)
  at org.junit.rules.TestWatcher.evaluate(TestWatcher.java:55)
  at org.junit.rules.RunRules.evaluate(RunRules.java:20)
  at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
  at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
  at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
  at org.junit.runners.ParentRunner.run(ParentRunner.java:238)
  at org.junit.runners.ParentRunner.schedule(ParentRunner.java:63)
  at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
  at org.junit.runners.ParentRunner.access[=15=]0(ParentRunner.java:53)
  at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:229)
  at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
  at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
  at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
  at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: java.lang.NullPointerException
  at org.apache.camel.impl.CamelPostProcessorHelper.matchContext(CamelPostProcessorHelper.java:84)
  at org.apache.camel.impl.DefaultCamelBeanPostProcessor.doWith(DefaultCamelBeanPostProcessor.java:181)
  at org.apache.camel.util.ReflectionHelper.doWithFields(ReflectionHelper.java:73)
  at org.apache.camel.impl.DefaultCamelBeanPostProcessor.injectFields(DefaultCamelBeanPostProcessor.java:168)
  at org.apache.camel.impl.DefaultCamelBeanPostProcessor.postProcessBeforeInitialization(DefaultCamelBeanPostProcessor.java:82)
  at org.apache.camel.spring.CamelBeanPostProcessor.postProcessBeforeInitialization(CamelBeanPostProcessor.java:148)
  ... 31 more

CamelBeanPostProcessor 显然有一个错误,当有多个上下文时返回空值!

if (contexts != null && contexts.size() == 1) {

@XmlTransient
private final DefaultCamelBeanPostProcessor delegate = new DefaultCamelBeanPostProcessor() {
    @Override
    public CamelContext getOrLookupCamelContext() {
        if (camelContext == null) {
            if (camelId != null) {
                LOG.trace("Looking up CamelContext by id: {} from Spring ApplicationContext: {}", camelId, applicationContext);
                camelContext = applicationContext.getBean(camelId, CamelContext.class);
            } else {
                // lookup by type and grab the single CamelContext if exists
                LOG.trace("Looking up CamelContext by type from Spring ApplicationContext: {}", applicationContext);
                Map<String, CamelContext> contexts = applicationContext.getBeansOfType(CamelContext.class);
                if (contexts != null && contexts.size() == 1) {
                    camelContext = contexts.values().iterator().next();
                }
            }
        }
        return camelContext;
    }

Camel 2.16.2 Spring 4.1.5 JDK 1.7 JDK 1.8

我也为此苦苦挣扎了一段时间。最后,我设法解决了一个变通方法,它允许我使用生产中使用的原始路由 xml 文件,但合并到单个 camel 上下文中以便能够将其用于测试。这样我就可以为 bean 端点注入模拟并通过在模拟上断言来检查完整的过程。

有两种不同的捆绑包。一个来自发票,另一个用于电子邮件。路由协调流程。

首先,我在生产 xml 文件中外部化了路由。 Spring 上下文 (invoicing-spring-context.xml) 和路由 (invoicing-routes.xml) 发票包文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    ...
    <camelContext id="invoicingCamelContext"
        xmlns="http://camel.apache.org/schema/spring">
        <routeContextRef ref="invoicingRoutes"/>
    </camelContext>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    <routeContext id="invoicingRoutes" xmlns="http://camel.apache.org/schema/spring">
        <route id="planner" autoStartup="true">
            <from uri="quartz://planner?cron=0+0+23+16+*+?" />
            <to uri="direct:invoicing" />
        </route>

        <route id="invoicing" autoStartup="true">
            <from uri="direct:invoicing?exchangePattern=InOut" />
            <to uri="bean:invoicer?method=generateInvoices" />
            <to uri="direct-vm:emailing" />
        </route>
    </routeContext>
</beans>

Spring 上下文 (emailing-spring-context.xml) 和路由 (emailing-routes.xml) 用于电子邮件捆绑:

<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    ...
    <camelContext id="emailingCamelContext"
        xmlns="http://camel.apache.org/schema/spring">
        <routeContextRef ref="emailingRoutes"/>
    </camelContext>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    <routeContext id="emailingRoutes" xmlns="http://camel.apache.org/schema/spring">
        <route id="emailing" autoStartup="true">
            <from uri="direct-vm:emailing" />
            <to uri="bean:emailer?method=createEmails" />
            <to uri="bean:emailer?method=sendEmails" />
        </route>
    </routeContext>
</beans>

然后出于测试目的,我创建了另一个 spring 上下文 (complete-process-test-spring-context.xml),它同时导入了路线文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    ...
    <camelContext id="completeProcessTestCamelContext"
        xmlns="http://camel.apache.org/schema/spring">      
        <routeContextRef ref="invoicingRoutes"/>
        <routeContextRef ref="emailingRoutes"/>
    </camelContext>
</beans>

测试 class 看起来像:

public class CompleteProcessTest extends CamelSpringTestSupport {
    @Test
    public void completeProcess() {
        ...

        invoicerMock.generateInvoices(EasyMock.isA(Exchange.class));
        emailerMock.createEmails(EasyMock.isA(Exchange.class));
        emailerMock.sendEmails(EasyMock.isA(Exchange.class));

        EasyMock.replay(invoicerMock);
        EasyMock.replay(emailerMock);

        this.template.requestBody(this.context.getEndpoint("direct://invoicing"), "");

        EasyMock.verify(invoicerMock);
        EasyMock.verify(emailerMock);
    }

    @Override
    protected AbstractApplicationContext createApplicationContext() {
        return new ClassPathXmlApplicationContext("classpath:/META-INF/spring/invoicing-routes.xml",
                "classpath:/META-INF/spring/emailing-routes.xml",
                "classpath:/META-INF/spring/complete-process-test-spring-context.xml");
    }

    ...
}

谢谢@Jorge-c,但是使用 routeContext 我们一直只有一个 CamelContext。

要在单元测试中使用乘法上下文,请不要使用 CamelSpringTestSupport,这是一个错误。

public class BaseSpringTest extends CamelSpringTestSupport {...}

使用“@RunWith(SpringJUnit4ClassRunner.class)”

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/camel-my-root-spring-config.xml")
public class BaseSpringJUnit4 
{
  @EndpointInject(uri = "direct:myroute", context="contextB")
  private Endpoint eFooTest;
}

这有效! 不要忘记在端点注释中明确地加上 context="contextB"

Camel 2.16.2 Spring 4.1.5 JDK 1.7 JDK 1.8