使用Apache Camel对FTP消费者进行单元测试

5
我有以下路由。在单元测试中,由于我没有可用的FTP服务器,我想使用camel的测试支持并向"ftp://hostname/input"发送一个无效的消息,并验证它失败并路由到"ftp://hostname/error"
我查阅了文档,主要讲解如何使用"mock:"端点,但我不确定在这种情况下该如何使用。
public class MyRoute extends RouteBuilder
{
    @Override
    public void configure()
    {
        onException(EdiOrderParsingException.class).handled(true).to("ftp://hostname/error");

        from("ftp://hostname/input")
            .bean(new OrderEdiTocXml())
            .convertBodyTo(String.class)
            .convertBodyTo(Document.class)
            .choice()
            .when(xpath("/cXML/Response/Status/@text='OK'"))
            .to("ftp://hostname/valid").otherwise()
            .to("ftp://hostname/invalid");
    }
}
4个回答

11

正如Ben所说,你可以设置一个FTP服务器并使用真实组件。FTP服务器可以嵌入,或者您可以在内部设置一个FTP服务器。后者更像是集成测试,您可能拥有一个专用的测试环境。

Camel在其测试工具包中非常灵活,如果您想构建一个不使用真实FTP组件的单元测试,则可以在测试前替换它。例如,在您的示例中,您可以将路由的输入端点替换为直接端点,以便更轻松地向路由发送消息。然后,您可以使用拦截器拦截发送到FTP端点的消息,并将消息转向。

测试工具包的部分建议提供了这些功能:http://camel.apache.org/advicewith.html。这也在《Camel in action》一书的第6章中讨论,如第6.3节所述,该节讨论了模拟错误。

在您的示例中,您可以执行以下操作:

public void testSendError() throws Exception {
    // first advice the route to replace the input, and catch sending to FTP servers
    context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
        @Override
        public void configure() throws Exception {
            replaceFromWith("direct:input");

            // intercept valid messages
            interceptSendToEndpoint("ftp://hostname/valid")
                .skipSendToOriginalEndpoint()
                .to("mock:valid");

            // intercept invalid messages
            interceptSendToEndpoint("ftp://hostname/invalid")
                .skipSendToOriginalEndpoint()
                .to("mock:invalid");
        }
    });

     // we must manually start when we are done with all the advice with
    context.start();

    // setup expectations on the mocks
    getMockEndpoint("mock:invalid").expectedMessageCount(1);
    getMockEndpoint("mock:valid").expectedMessageCount(0);

    // send the invalid message to the route
    template.sendBody("direct:input", "Some invalid content here");

    // assert that the test was okay
    assertMockEndpointsSatisfied();
}

从Camel 2.10版本开始,我们将在使用advice时使拦截和模拟变得更加容易。同时,我们正在引入一个存根组件。http://camel.apache.org/stub


我已经将端点定义为“ftp://host/incoming?move=processed”。如何确保文件已移动到“processed”目录?我该如何拦截它?Camel是否为“processed”创建了单独的端点? - Aravind Yarram
我还有两个from(..)的定义(一个用于定义处理采购订单的路由,另一个用于定义处理发票的路由),在这种情况下,replaceFromWith(...)会替换这两个路由吗? - Aravind Yarram
我找到了第二个评论的答案。我需要在context.getRouteDefinitions().get(0)方法中使用适当的索引。请帮我看一下第一个评论。 - Aravind Yarram

5

快来看看MockFtPServer吧!

<dependency>
    <groupId>org.mockftpserver</groupId>
    <artifactId>MockFtpServer</artifactId>
    <version>2.2</version>
    <scope>test</scope>
</dependency>

使用这个工具,你可以模拟各种行为,如权限问题等:

例子:

fakeFtpServer = new FakeFtpServer();

fakeFtpServer.setServerControlPort(FTPPORT);

FileSystem fileSystem = new UnixFakeFileSystem();
fileSystem.add(new DirectoryEntry(FTPDIRECTORY));
fakeFtpServer.setFileSystem(fileSystem);
fakeFtpServer.addUserAccount(new UserAccount(USERNAME, PASSWORD, FTPDIRECTORY));

...

assertTrue("Expected file to be transferred", fakeFtpServer.getFileSystem().exists(FTPDIRECTORY + "/" + FILENAME)); 

1

看一下这个单元测试以及同一目录中的其他测试...它们将向您展示如何搭建本地FTP服务器进行测试,以及如何使用CamelTestSupport验证场景等。

例如单元测试...

https://svn.apache.org/repos/asf/camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FromFileToFtpTest.java

它扩展了这个测试支持类...

https://svn.apache.org/repos/asf/camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpsServerTestSupport.java


0
在我们的项目中,我们不会创建模拟FTP服务器来测试路由,而是使用可以被本地开发和单元测试中的file Camel组件替换的属性。
您的代码应该如下所示:
public class MyRoute extends RouteBuilder
{
    @Override
    public void configure()
    {
        onException(EdiOrderParsingException.class)
          .handled(true)
          .to("{{myroute.error}}");

        from("{{myroute.input.endpoint}}")
            .bean(new OrderEdiTocXml())
            .convertBodyTo(String.class)
            .convertBodyTo(Document.class)
            .choice()
              .when(xpath("/cXML/Response/Status/@text='OK'"))
                .to("{{myroute.valid.endpoint}}}")
              .otherwise()
                .to("{{myroute.invalid.endpoint}}");
    }
}

而在本地和系统测试中,我们使用在属性文件中声明的文件端点:

myroute.input.endpoint=file:/home/user/myproject/input
myroute.valid.endpoint=file:/home/user/myproject/valid
myroute.invalid.endpoint=file:/home/user/myproject/invalid
myroute.error=file:/home/user/myproject/error

或者在JUnit CamelTestSupport中,您可以使用useOverridePropertiesWithPropertiesComponent方法来设置要覆盖的属性。

作为替代方案,您也可以使用“直接”路由,但是您可能会错过一些可以通过单元测试进行测试的文件选项。

我们只通过设置如下属性来测试与真实系统的FTP连接:

myroute.input.endpoint=ftp://hostname/input
myroute.valid.endpoint=ftp://hostname/valid
myroute.invalid.endpoint=ftp://hostname/invalid
myroute.error=ftp://hostname/error

通过这个,你也可以为不同的配置设置不同的环境,例如生产服务器和集成测试环境。

生产环境属性示例:

myroute.input.endpoint=ftp://hostname-prod/input
myroute.valid.endpoint=ftp://hostname-prod/valid
myroute.invalid.endpoint=ftp://hostname-prod/invalid
myroute.error=ftp://hostname-prod/error

在我看来,使用文件端点来简化JUnit代码并仅测试路由而不是连接是完全可以接受的。
测试连接更像是集成测试,应该在连接到真实外部系统的真实服务器上执行(在您的情况下是FTP服务器,但也可以是其他端点/系统)。
通过使用属性,您还可以针对每个环境配置不同的URL(例如:我们有3个测试环境和一个生产环境,所有这些环境都具有不同的端点)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接