【问题标题】:Test a Camel sFTP endpoint测试 Camel sFTP 端点
【发布时间】:2018-03-19 09:21:07
【问题描述】:

我有以下路线:

public void configure() throws Exception {
    from(ftpEndpoint)
        .routeId("import-lib-files")
        .log(INFO, "Processing file: '${headers.CamelFileName}' from Libri-FTP")
        .choice()
        .when(method(isFilenameAlreadyImported))
            .log(DEBUG, "'${headers.CamelFileName}' is already imported.")
        .endChoice()
        .otherwise()
        .bean(method(unzipLibFile))
        .bean(method(persistFilename))
        .log(DEBUG, "Import file '${headers.CamelFileName}'.")
        .endChoice()
    .end()
    .end();
}

unzipLibFile 处理器 bean 中,来自 ftp 的文件被解压缩并写入 HD。

我想测试(集成测试)这条路线,比如:

    1. 将文件复制到 ftp
    1. 开始路线
    1. 评估“结果”

我喜欢:

  @Before
  public void setUp() throws Exception {
    // delete test-file from sftp
    final String uploaded = ftpPath + "/" + destination + "/libri-testfile.zip";
    final File uploadedFile = new File(uploaded);
    uploadedFile.delete();

    // delete unzipped test-file
    final String unzippedFile = unzipped + "/libri-testfile.xml";
    final File expectedFile = new File(unzippedFile);
    expectedFile.delete();

    // delete entries from db
    importedLibFilenameRepository.deleteAll();

    // copy file to ftp
    final File source =
    new ClassPathResource("vendors/references/lib.zip/libri-testfile.zip").getFile();
    final String target = ftpPath + "/" + destination + "/libri-testfile.zip";
    FileUtils.copyFile(new File(source.getAbsolutePath()), new File(target));
  }


  @Test
  @Ignore
  public void testStuff() throws Exception {
    // Well here is a problem, I can't fix at the moment
    // the Camel-Context within the SpringContext get started when the tests starts
    // during this process the Camel-Routes are executed and because i copied the file to
    // the ftp all is fine... but I don't want to have a sleep in a test, I want to start the
    // route (like commented code beneath the sleep)
    Thread.sleep(2000);

//    final Map<String, Object> headers = Maps.newHashMap();
//    headers.put("CamelFileName", "libri-testfile.zip");
//
//    final File file =
//        new ClassPathResource("vendors/references/lib.zip/libri-testfile.zip").getFile();
//    final GenericFile<File> genericFile =
//        FileConsumer.asGenericFile(file.getParent(), file, StandardCharsets.UTF_8.name(), false);
//
//    final String uri = libFtpConfiguration.getFtpEndpoint();
//    producer.sendBodyAndHeaders(uri, InOut, genericFile, headers);

    // test if entry was made in the database
    final List<ImportedLibFilename> filenames = importedLibFilenameRepository.findAll();
    assertThat(filenames).usingElementComparatorIgnoringFields("id", "timestamp")
    .containsExactly(expectedFilename("libri-testfile.zip"));

    // test if content of unzipped file is valid
    final String expected = unzipped + "/libri-testfile.xml";
    final Path targetFile = Paths.get(expected);
    final byte[] encoded = Files.readAllBytes(targetFile);
    final String actualFileContent = new String(encoded, Charset.defaultCharset());

    final String expectedFileContent = "This is my little test file for Libri import";
    assertThat(actualFileContent).isEqualTo(expectedFileContent);
  }

  private ImportedLibFilename expectedFilename(final String filename) {
    final ImportedLibFilename entity = new ImportedLibFilename();
    entity.setFilename(filename);
    return entity;
  }

问题是:
所有骆驼路线都是自动启动的,因为我将文件复制到了 FTP,所以测试是绿色的。但是我的测试中有一个#sleep,这是我不想要的。我不想开始骆驼路线,只开始我需要的路线。

我的问题是:

    1. 如何防止 Camel-Routes 自动启动
    1. 注释代码(在测试方法中)是手动启动路由的正确方法吗?
    1. 使用 ftp 测试骆驼路由的最佳做法是什么

【问题讨论】:

  • 您是否已经尝试按照here 的说明在您的路线上定义.noAutoStartup().autoStartup(false)
  • 我不想在路线上出现noAutoStartup()autoStartup(false)。我只想在测试用例中避免这种行为。
  • 使用 ControlBus 组件在测试设置之前停止路由并在测试之前立即启动它?

标签: java testing apache-camel


【解决方案1】:
  1. 在您的路由中使用.autoStartup(yourVariable) 以使其启动可配置。在正常环境中将变量设置为true,在您的测试用例中设置为false
  2. 我没有看到启动路线的代码?!?
  3. 好吧,退后一步。考虑拆分您的 FTP 路由。出于测试和其他原因:

例如,将路由拆分为 FTP 和处理路由。第一个只做 FTP 传输,然后将收到的消息发送到处理路由(例如direct: 路由)。

好处:

  • SRP:两条路线只做一件事,你可以专心做这件事。
  • 可测试性:您可以通过向处理路由的direct: 端点发送消息来轻松测试处理路由。测试也可以专注于一件事。
  • 可扩展性:假设有一个新的输入通道(JMS、HTTP 等)。然后,您只需添加另一个输入路由,该路由也发送到您的处理路由。完毕。

如果您真的想测试从 FTP 文件删除到结束的整个过程,请考虑使用 Citrus test framework 或类似工具。骆驼路线测试(在我看来)是一种“骆驼路线的单元测试”,而不是完整的集成测试。

【讨论】:

  • 像第 3 点提到的那样拆分路线听起来是个好主意,我一有时间就会尝试。我会很快再次报告。开始路线不是正确的选择。更像是在频道上发消息 --> producer.sendBodyAndHeaders(uri, InOut, genericFile, headers);
  • @SleepyX667 Camel 提供了ProducerTemplate,您可以在其中发送任意内容和标头到路由,如果这是您正在寻找的内容
  • @Roman Vottner 这正是我想要做的。见注释行producer.sendBodyAndHeaders(uri, InOut, genericFile, headers);
【解决方案2】:

感谢@burki...

他的建议拆分路线(单一责任)帮助我解决了我的问题:

路线如下:

从 sFTP 消费的“主路由”:

  @Override
  public void configure() throws Exception {
    // @formatter:off
    from(endpoint)
      .setHeader("Address", constant(address))
      .log(INFO, "Import Libri changeset: Consuming from '${headers.Address}' the file '${headers.CamelFileName}'.")
      .to("direct:import-new-file");
    // @formatter:on
  }

第一个子路线:

  @Override
  public void configure() throws Exception {
    // @formatter:off
    from("direct:import-new-file")
        .choice()
          .when(method(isFilenameAlreadyImported))
          .log(TRACE, "'${headers.CamelFileName}' is already imported.")
        .endChoice()
        .otherwise()
          .log(TRACE, "Import file '${headers.CamelFileName}'.")
          .multicast()
          .to("direct:persist-filename", "direct:unzip-file")
        .endChoice()
      .end()
    .end();
    // @formatter:on
  }

两个多播:

  @Override
  public void configure() throws Exception {
    // @formatter:off
      from("direct:persist-filename")
        .log(TRACE, "Try to write filename '${headers.CamelFileName}' to database.")
        .bean(method(persistFilename))
      .end();
    // @formatter:on
  }

  @Override
  public void configure() throws Exception {
    // @formatter:off
      from("direct:unzip-file")
        .log(TRACE, "Try to unzip file '${headers.CamelFileName}'.")
        .bean(method(unzipFile))
      .end();
    // @formatter:on
  }

通过这种设置,我可以编写如下测试:

  @Test
  public void testRoute_validExtractedFile() throws Exception {
    final File source = ZIP_FILE_RESOURCE.getFile();
    producer.sendBodyAndHeaders(URI, InOut, source, headers());

    final String actual = getFileContent(unzippedPath, FILENAME);
    final String expected = "This is my little test file for Libri import";
    assertThat(actual).isEqualTo(expected);
  }

  @Test
  public void testRoute_databaseEntryExists() throws Exception {
    final File source = ZIP_FILE_RESOURCE.getFile();
    producer.sendBodyAndHeaders(URI, InOut, source, headers());

    final List<ImportedFilename> actual = importedFilenameRepository.findAll();
    final ImportedFilename expected = importedFilename(ZIPPED_FILENAME);
    assertThat(actual).usingElementComparatorIgnoringFields("id", "timestamp")
    .containsExactly(expected);
  }

  private String getFileContent(final String path, final String filename) throws IOException {
    final String targetFile = path + "/" + filename;
    final byte[] encodedFileContent = Files.readAllBytes(Paths.get(targetFile));
    return new String(encodedFileContent, Charset.defaultCharset());
  }

  private Map<String, Object> headers() {
    final Map<String, Object> headers = Maps.newHashMap();
    headers.put("CamelFileName", ZIPPED_FILENAME);
    return headers;
  }

我可以使用ProducerTemplate(生产者)启动骆驼路线,并将消息发送到直接端点(而不是 ftp 端点)。

【讨论】:

    猜你喜欢
    • 2018-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多