【问题标题】:Unit testing camel routes using camel test support使用骆驼测试支持对骆驼路线进行单元测试
【发布时间】:2022-01-04 17:13:32
【问题描述】:

我想正确的单元和集成测试用例来测试所有的功能流程。有人可以分享您对我如何创建一个模拟端点来监视目录以创建文件并通过多个路由进一步处理它们并将文件移动到不同目录的想法。

  1. 监控/hello 目录以创建新文件。
  2. 如果存在基于文件前缀的文件,则使用容器名称更新标头。
  3. 根据文件名上传到适当的 azure blob 容器。
  4. 进行 api 调用并将文件处理后移动到 /success 目录。

FileWatcherRoute.java

@Service
public class FileWatcherRoute extends RouteBuilder {

  @Value("${watcher-base-url}")
  private String baseUrl;

  @Value("${watcher-subscription-key}")
  private String subscriptionKey;

  @Override
  public void configure() {

    Processor logResponse = exchange -> log
        .info("The response code is: {}", exchange.getIn().getHeader(Exchange.HTTP_RESPONSE_CODE));

    from("file-watch:hello?events=CREATE&antInclude=**/*.csv&recursive=true")
        .routeId("fileWatch")
        .to("direct:updateHeaders")
        .end();

    from("direct:updateHeaders")
        .routeId("updateHeaders")
        .choice()

            .when((exchange -> exchange.getIn().getHeader(Exchange.FILE_NAME).toString().trim().matches("\\d{8}_\\d{4}(_Inventory.csv)")))
            .setHeader("CamelAzureStorageBlobContainerName", constant(AppConstants.STORE))

                        .when(exchange -> exchange.getIn().getHeader(Exchange.FILE_NAME).toString().trim().matches("\\d{8}-\\d{6}_Idle_Inventory_\\d{4}.csv"))
            .setHeader("CamelAzureStorageBlobContainerName",constant(AppConstants.IDLE_FILE))

        .toD("direct:uploadFileToBlob")
        .end();

    from("direct:uploadFileToBlob")
        .routeId("uploadFile")
        .log("Container Name: ${header.CamelAzureStorageBlobContainerName}")
        .toD("azure-storage-blob://{accName}/${header.CamelAzureStorageBlobContainerName}?blobName=${header.CamelFileName}&operation=uploadBlockBlob&serviceClient=#serviceClient")
        .to("direct:startRestApi")
        .log("${header.CamelFileName} Uploaded to ${header.CamelAzureStorageBlobContainerName} Container Successfully")
        .end();


    from("direct:startRestApi")
        .routeId("restCall")
        .setHeader(Exchange.HTTP_METHOD, constant("GET"))
        .setHeader("Content-Type",constant("application/json"))
        .setHeader("Ocp-Apim-Subscription-Key",constant(subscriptionKey))
        .to(baseUrl)
        .to("direct:processedFiles")
        .process(logResponse)
        .end();

    from("direct:processedFiles")
        .routeId("fileProcessing")
        .choice()
        .when(exchange -> exchange.getIn().getHeader(Exchange.HTTP_RESPONSE_CODE).equals(200))
        .to("file://success")
        .otherwise()
        .to("file://error")
        .end();
  }
}

FileWatcherRouteTest.java

@CamelSpringBootTest
@SpringBootTest
@MockEndpoints
@UseAdviceWith
public class FileWatcherRouteTest {

  @Autowired
  CamelContext camelContext;

  @Autowired
  ProducerTemplate producerTemplate;

  @EndpointInject("mock:processedFiles")
  MockEndpoint mockEndpoint;

  @Test
  void when_new_file_created_should_update_header_with_container_name_and_upload_to_container() throws Exception {

    AdviceWith.adviceWith(camelContext, "fileWatch", routeBuilder -> {
      routeBuilder.replaceFromWith("direct:file-watch");
    });

    camelContext.start();
    mockEndpoint.assertIsSatisfied();

  }

}

【问题讨论】:

    标签: spring-boot apache-camel azure-storage spring-camel camel-test


    【解决方案1】:

    MockEndpoint 仅支持生产者端点。这是因为它旨在执行对 Exchange 和消息的断言。

    例如:

    • 给定的 MockEndpoint 是否接收到正确数量的消息
    • 邮件正文是否与预期相符。
    • 向 MockEndpoint 提供所需标头的位置

    在测试路由时,您应该使用ProducerTemplate send 方法来启动被测路由,并使用您想要测试的任何正文、标头和属性。

    在您的情况下,您可以使用 java 文件操作从测试资源文件夹中读取文件作为字符串、字节数组或流,并将其作为正文连同 headersCamelFileNameCamelFileEventType 一起发送到目标路由。

    我还建议将您的直接路由视为函数,以使其更易于测试。这意味着您可以拥有仅以正确顺序调用子路由的父路由,而不是从一个路由跳转到另一个路由。

    from("file-watch:hello?events=CREATE&antInclude=**/*.csv&recursive=true")
        .routeId("fileWatch")
        .to("direct:processNewFile")
    
    // "Parent route"
    from("direct:processNewFile")
        .routeId("processCreatedFile")
        .to("direct:updateHeaders")
        .to("direct:uploadFileToBlob")
        .to("direct:processedFiles")
    

    这使您可以轻松地为 direct:updateHeadersdirect:uploadFileToBlobdirect:processedFiles 编写单独的测试

    例如:

    • 测试direct:uploadFileToBlob 是否在所需标头之一丢失或因某种原因无效时引发适当的异常。
    • 测试 direct:uploadFileToBlob 使用 azure 正确处理连接异常。
    • 测试direct:processedFiles 是否将文件放置到正确的文件夹中。

    如果您想测试文件输出,您可以使用 JUnits TemporaryFolder 创建临时文件夹,然后您可以将其作为文件生产者端点的目标。路由完成后,您可以使用基本的assertEquals 和文件操作来检查输出是否符合您的测试要求。

    Apache Commons IO 也是非常方便的库,用于测试从资源中读取文件并将文件复制到 TemporaryFolder。

    文件、ProducerTemplate、Commons IO 和 CamelTestSupport 的几个示例:

    package com.example;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.InputStream;
    import java.nio.charset.StandardCharsets;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Properties;
    
    import org.apache.camel.Exchange;
    import org.apache.camel.RoutesBuilder;
    import org.apache.camel.builder.AdviceWithRouteBuilder;
    import org.apache.camel.builder.RouteBuilder;
    import org.apache.camel.component.mock.MockEndpoint;
    import org.apache.camel.test.junit4.CamelTestSupport;
    import org.apache.commons.io.FileUtils;
    import org.junit.Rule;
    import org.junit.Test;
    import org.junit.rules.TemporaryFolder;
    
    public class ExampleTest  extends CamelTestSupport {
        
        @Rule
        public TemporaryFolder temporaryFolder = new TemporaryFolder();
        
        File inputFolder;
        File outputFolder;
    
        @Test
        public void temporaryFolderExampleTest() throws Exception {
    
            context.adviceWith(context.getRouteDefinition("processInputFile"), 
                new AdviceWithRouteBuilder(){
    
                    @Override
                    public void configure() throws Exception {
                        
                        replaceFromWith("direct:start");
    
                        weaveAddLast()
                            .to("mock:result");
                    }
                }
            );
    
            MockEndpoint resultMockEndpoint = getMockEndpoint("mock:result");
            resultMockEndpoint.expectedMessageCount(1);
            resultMockEndpoint.message(0).body().isEqualTo("Hello world!");
    
            InputStream body = fetchFileFromResourcesFolderAsStream("test-files/Hello.txt");
            Map<String, Object> headers = new HashMap<>();
            headers.put(Exchange.FILE_NAME, "Hello.txt");
    
            startCamelContext();
            template.sendBodyAndHeaders("direct:start", body, headers);
    
            resultMockEndpoint.assertIsSatisfied();
    
            File resultFile = new File(outputFolder, "Hello.txt");
            assertEquals(true, resultFile.exists());
    
            // FileUtils from commons-io/commons-io/2.11.0
            String result = FileUtils.readFileToString(resultFile, StandardCharsets.UTF_8);
            assertEquals("Hello world!", result);
        }
    
        @Test
        public void pollEnrichExampleTest() throws Exception {
    
            context.adviceWith(context.getRouteDefinition("pollEnrichExample"), 
                new AdviceWithRouteBuilder(){
    
                    @Override
                    public void configure() throws Exception {
    
                        weaveAddLast()
                            .to("mock:result");
                    }
                }
            );
    
            MockEndpoint resultMockEndpoint = getMockEndpoint("mock:result");
            resultMockEndpoint.expectedMessageCount(1);
            resultMockEndpoint.message(0).body().isEqualTo("Hello");
    
            File resourceFile = fetchFileFromResourcesFolder("test-files/Hello.txt");
            File testFile = new File(outputFolder, "Hello.txt");
            FileUtils.copyFile(resourceFile, testFile);
            
            startCamelContext();
            template.sendBody("direct:pollEnrichExample", null);
    
            resultMockEndpoint.assertIsSatisfied();
        }
    
        @Override
        protected RoutesBuilder createRouteBuilder() throws Exception {
        
            return new RouteBuilder() {
    
                @Override
                public void configure() throws Exception {
                 
                    from("file:{{file.input}}")
                        .routeId("processInputFile")
                        .convertBodyTo(String.class)
                        .setBody(simple("${body} world!"))
                        .log("${body}")
                        .to("file:{{file.output}}");
    
                    from("direct:pollEnrichExample")
                        .routeId("pollEnrichExample")
                        .pollEnrich("file:{{file.output}}", 3000)
                        .convertBodyTo(String.class)
                        .log("${body}");
                }
            };
        }
    
        // use of placeholder properties and configuration files is highly encouraged
        @Override
        protected Properties useOverridePropertiesWithPropertiesComponent() {
            
            try {
                inputFolder = temporaryFolder.newFolder("input");
                outputFolder = temporaryFolder.newFolder("output");
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            Properties prop = new Properties();
            prop.setProperty("file.input", inputFolder.getPath());
            prop.setProperty("file.output", outputFolder.getPath());
            return prop;
        }
    
        @Override
        public boolean isUseAdviceWith() {
            return true;
        }
    
        public File fetchFileFromResourcesFolder(String pathInResources){
            
            ClassLoader classLoader = ExampleTest.class.getClassLoader();
            return new File(classLoader.getResource(pathInResources).getFile());
        }
    
        public InputStream fetchFileFromResourcesFolderAsStream(String pathInResources){
    
            try {
                return new FileInputStream(fetchFileFromResourcesFolder(pathInResources));   
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    }
    
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.11.0</version>
    </dependency>
    

    以上使用 Camel 3.4.4 的示例

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-19
      • 1970-01-01
      相关资源
      最近更新 更多