Exemplar 自动化示例测试简介

目录

简介

这篇文章介绍了名为 Exemplar 的新库。Exemplar 的目标是确保用户获得您期望看到的输出。它处理示例发现、规范化(语义上等效的结果,可能来自不同的环境)和灵活的输出验证。它调用环境中要调用的任何命令行工具。例如,您也可以调用 curl 来验证服务 API 响应。

Gradle 使用此库来验证文档和指南中的示例,并消除集成测试中的样板代码。

Exemplar 可以使用 JUnit 测试运行器(推荐)或使用其 API 进行配置。请参阅下面的示例和 Exemplar GitHub 仓库 中的示例。

Exemplar Information Flow

Exemplar 的用例 #

文档示例的准确性非常重要。想象一下,当试图学习新事物时,示例却无法工作,那将是多么令人沮丧。

我们认为 Exemplar 最适合作为文档检查机制。然而,它并非旨在替代其他形式的集成测试。

Exemplar 是独一无二的,因为它允许发现嵌入在结构化文档中的示例,以及 OutputNormalizers(捆绑的和自定义的),方便在“更传统”的集成测试中使用示例项目(通过 @UsesSample("path/to/sample")),并允许在执行之前以编程方式修改示例(用于额外的环境设置,或许是 SampleModifier)。

Exemplar 专注于日志记录和退出代码输出,验证其他副作用很麻烦(检查创建的文件可能需要额外的命令)。此外,尽管 Exemplar 可以用于任何工具,但它仅具有用于 JVM 项目的 API。

示例项目测试入门 #

Exemplar 中的 sample-discovery 库会将示例根目录下包含 *.sample.conf 文件的任何目录视为示例项目。

示例配置文件以 HOCON 编写,并告诉 sample-check 如何运行示例项目。它可以很简单

executable: echo
args: "Hello World"

… 或者更复杂,包含多个步骤

commands: [{
  executable: gradle
  args: originalInputs incrementalReverse
  expected-output-file: originalInputs.out
  allow-additional-output: true
}, {
  executable: gradle
  args: --quiet removeOutput incrementalReverse
  expected-output-file: incrementalTaskRemovedOutput.out
  allow-disordered-output: true
}]

请参阅文档中的 示例配置参考,了解可用的选项。

然后,JUnit 测试声明要使用的 @SampleRunner 以及示例测试的其他方面。

@RunWith(GradleSamplesRunner.class)
@SamplesRoot("src/docs/samples")
@SamplesOutputNormalizers({JavaObjectSerializationOutputNormalizer.class, FileSeparatorOutputNormalizer.class})
public class SamplesIntegrationTest {}

执行此测试将发现并运行外部(即,非嵌入式)示例项目,并生成您期望的 JUnit 测试报告。

嵌入式示例测试入门 #

Exemplar 可以使用 Asciidoctor 的 AST 在 Asciidoctor 源文件中发现示例。这是一个使用 SampleModifiers 以及设置示例环境的示例。

.Sample title
====
[.testable-sample]       (1)
=====
.hello.rb                (2)
[source,ruby]            (3)
-----
puts "hello, #{ARGV[0]}" (4)
-----

[.sample-command]        (5)
-----
$ ruby hello.rb world    (6)
hello, world             (7)
-----
=====
====

请阅读 嵌入式示例文档,了解允许其工作的 Asciidoctor 元素。

我们使用 EmbeddedSamplesRunner 运行此测试,指向 docs/ 根目录,Exemplar 将在其中查找文档文件。

@RunWith(EmbeddedSamplesRunner.class)
@SamplesRoot("docs")
@SampleModifiers({SetupEnvironmentSampleModifier.class, ExtraCommandArgumentsSampleModifier.class})
public class EmbeddedSamplesIntegrationTest {}

执行此测试会将代码从 Asciidoctor 提取到临时项目,运行它,并验证它是否与文档中声明的输出匹配。

采纳和贡献 #

最好的入门方法是阅读 Exemplar 文档,并查看 Exemplar 的示例示例测试

在撰写本文时,Exemplar 的版本为 0.6,这意味着 API 在 v1.0 之前可能会发生更改。也就是说,鉴于 API 已经过一些修订并已被 Gradle 采用,因此在 1.0 版本之前不太可能出现重大更改。

以下是一些可能是该库的良好扩展的功能,如果您渴望提交想法或向 gradle/exemplar GitHub 仓库 提交拉取请求,我将非常乐意获得您的帮助。

  • 更多标准规范化器,例如日期、时间或持续时间规范化
  • 为搜索索引生成结构化元数据
  • 允许预期输出内联在示例配置文件中
  • 嵌入在 Markdown 或其他文档格式中的示例发现 (#2938)

如果您希望获得关于采纳或贡献此项目的指导,请通过 Twitter 上的 @eriwen 与我联系。

讨论