概览
本页介绍了在 BUILD.bazel 文件中定义和构建测试和 test_suite() 目标的样式和最佳实践,以及如何将它们连接到 GN build 图。本页面上的指南是对常见 Bazel 样式指南和最佳实践以及 BUILD.bazel 文件样式指南的补充。如需了解如何定义、构建和运行主机和设备测试目标,请参阅 Bazel 测试。
本页面是 Bazel 样式指南和最佳实践的一部分。
测试套件结构
在 Fuchsia 的 Bazel 构建图中,测试分为三层,并与构建相关联:
单个软件包:每个
BUILD.bazel文件都会定义私有*_test目标,并通过名为"tests"和/或"host_tests"的软件包test_suite()目标公开这些目标(请参阅在单个软件包中定义测试和测试套件)。汇总软件包:针对某个区域或子系统组的专用软件包,可将软件包
test_suite()目标从单个软件包分组到汇总test_suite()目标(例如"bluetooth_tests")。GN build 图集成:虽然 GN 驱动整体 build,但
BUILD.gn中的bazel_test_suite()目标会引用同一目录中的聚合test_suite()目标,以便将测试包含在tests.json中(请参阅将 Bazel 测试套件连接到 GN build 图)。
不得在定义单个测试的软件包之外引用这些测试,并且软件包不会以递归方式包含其子软件包中的测试或 test_suite() 目标。如需了解此结构背后的基本原理,请参阅为什么只公开 test_suite() 目标?
示例
//src/connectivity/bluetooth/lib/packet-encoding/BUILD.bazel(单独的软件包):
test_suite(
name = "tests",
tests = [], # Include all tests in the package.
visibility = ["//src/connectivity/bluetooth:__pkg__"],
)
# Private test target(s) automatically included in ":tests" above.
...
//src/connectivity/bluetooth/BUILD.bazel(聚合软件包):
test_suite(
name = "bluetooth_tests",
tests = [
"//src/connectivity/bluetooth/lib/packet-encoding:tests",
...
],
)
//src/connectivity/bluetooth/BUILD.gn(GN 集成):
import("//build/bazel/bazel_test_suite.gni")
bazel_test_suite("bazel_tests") {
target_tests = [ "//src/connectivity/bluetooth:bluetooth_tests" ]
}
group("tests") {
testonly = true
deps = [
":bazel_tests",
...
]
}
定义和构建 Bazel 测试和测试套件
目标名称和可见性
目标名称:测试目标的名称始终以
"_test"结尾。Bazeltest_suite()目标 的名称应为 或 ,或以"tests"或"host_tests"结尾(视情况而定)。测试目标可见性:不得在定义单个测试的软件包之外引用这些测试。测试目标必须始终具有软件包私有可见性,这通常是默认设置(请参阅目标可见性)。
test_suite()目标可见性:test_suite()目标应仅对包含依赖于它们的聚合test_suite()目标(在其tests属性中)的软件包可见。始终使用:__pkg__,而不是:__subpackages__。引用测试和
test_suite()目标:测试和test_suite()目标只能由test_suite()目标(在其tests属性中)引用。请勿将其放入filegroup()或类似容器中。
平台和 CPU 架构兼容性
目标平台兼容性 (target_compatible_with)
测试目标可以且应该酌情指定 target_compatible_with(例如,在主机测试中指定 target_compatible_with = HOST_OS_CONSTRAINTS;请参阅 target_compatible_with)。
不过,请勿依赖 Bazel 测试兼容性来过滤测试目标并仅运行兼容的目标。具体而言,请勿将针对不同平台的测试分组到单个 test_suite() 中(或将包含针对不同平台的测试的 test_suite() 目标分组到单个 test_suite() 中)。
test_suite() 目标是否应指定 target_compatible_with 待定。
CPU 架构
尽可能避免为每个 CPU 架构等定义单独的测试或 test_suite() 目标。理想情况下,可以针对所有 CPU 架构构建单个测试目标。
在极少数情况下,可能会有特定于 CPU 架构的测试,例如针对用汇编语言编写的 SIMD 库的单元测试。在这种情况下,请向 Build 团队寻求指导。(一旦我们对这些场景有了一些经验,就可以更新此文本。)
在各个软件包中定义测试和测试套件
公开软件包 test_suite() 目标
每个 Bazel 软件包(BUILD.bazel 文件)都应公开一个或多个软件包 test_suite() 目标,这些目标依赖于该软件包中定义的测试(在单个 BUILD.bazel 文件中,而不是在其任何子软件包中)。
这些目标应命名为 "tests" 或 "host_tests",具体取决于目标平台。所有此类 test_suite() 目标都应是 BUILD.bazel 文件中的第一个目标,最好紧跟在 load() 和 package() 语句之后。
包含软件包中的所有测试
Bazel 软件包中定义的所有测试都必须是同一软件包中某个 test_suite() 目标的依赖项。(未来,此要求可能会更改为“至少一个”。)这包括由用于定义非测试目标的宏定义的测试。
如果软件包中的所有测试都应位于给定的 test_suite() 中,请指定一个空的 tests 属性,并添加注释说明软件包中的所有测试都包含在内:
test_suite(
name = "tests",
tests = [], # Include all tests in the package.
visibility = [...],
)
如果可能,这种方法更易于维护,在添加测试时少了一个步骤,并可确保不会遗漏测试。
在所有软件包中定义默认 test_suite()
在可能的情况下,所有 BUILD.bazel 文件都应具有名为 "tests" 或 "host_tests" 且具有空 tests 属性 (tests = []) 的 test_suite(),无论它们是否明确定义了测试。每个此类 test_suite() 都应从适当的聚合 test_suite() 目标中引用。
这样可以确保将来添加到软件包中的任何测试(无论是显式添加还是通过宏添加)都会包含在 build 图中。如果我们能够强制执行此操作,还可以防止忘记将测试添加到 build 图中。
此规则的主要例外情况是同时包含 Fuchsia 和主机测试的软件包,因为目前无法在这两种情况下使用相同的测试目标,因此无法使用空的 tests 属性机制。
修改具有空 tests 属性的 test_suite()
修改 tests 属性
为空或未指定的现有 test_suite() 时,您必须确保该软件包
定义的所有测试都明确添加到此 或软件包中的其他 test_suite()。只有在软件包的测试在多个 test_suite() 目标之间划分时,才会发生这种情况。
不依赖于子软件包
请勿依赖软件包子软件包中的测试或 test_suite() 目标。
聚合 test_suite() 目标除外,但单个软件包通常不应同时具有这两种类型的 test_suite() 目标。
这与 GN 中的常见模式不同,在 GN 中,group("tests") 依赖于其子目录中的所有此类目标,这些目标以递归方式执行相同的操作。我们不希望在 Bazel 中重复这种模式。
聚合 test_suite() 目标
这些 test_suite() 目标会将各个软件包(例如,针对某个区域或子系统)中的 test_suite() 目标分组,并且应具有描述其范围和/或用途的名称。它们仍应视情况以 "tests" 或 "host_tests" 结尾。每种目标可能不止一个。举个假设的例子,"bluetooth_tests" 可能适用于所有产品,而 "bluetooth_le_tests" 可能包含要在支持蓝牙 LE 的产品上运行的其他测试。
一般来说,包含此类聚合 test_suite() 目标的软件包(BUILD.bazel 文件)本身不应定义其他目标,尤其是测试目标。例如://src/connectivity/bluetooth/。(另请注意,BUILD.gn 文件仅定义 group() 目标。)
根据汇总目标
未来,本部分将介绍每个产品的测试软件包中可选的进一步汇总和包含。
目前,系统会使用依赖于 bazel_test_suite() 目标的 GN 测试 group() 目标的现有依赖项。
Rust 单元测试
在 GN 中,rustc_*() 模板接受 with_unit_tests 参数,当该参数为 true 时,系统会根据源文件中的测试自动生成单元测试目标。
将 Bazel 测试套件连接到 GN build 图
使用 bazel_test_suite() GN 模板定义的目标应仅引用在同一目录(位于 BUILD.gn 文件旁边的 BUILD.bazel 文件中)中定义的聚合 test_suite() 目标。它们不得引用单个测试。
每个聚合 test_suite() 目标都必须在同一目录(位于 BUILD.bazel 文件旁边的 BUILD.gn 文件中)中由 bazel_test_suite() GN 模板定义的目标的相应字段中引用,以便其测试包含在 tests.json 中。使用以 "//" 开头的完整 Bazel 标签,而不是相对 ":" 标签(例如 "//src/connectivity/bluetooth:bluetooth_tests"),并按如下方式在 GN 模板的参数中引用目标:
名称以
"host_tests"结尾的目标:host_tests所有其他(以
"tests"结尾):target_tests
附录:为什么只公开 test_suite() 目标?
仅公开 test_suite() 目标(而非单个测试)的优势包括:
visibility只需要为test_suite()目标指定,而不需要为每个单独的测试目标指定。这对于支持
with_host_unit_tests和/或with_unit_tests的宏(例如 Fuchsia 的rustc_*()和ffx_plugin())尤为重要(请参阅 Rust 单元测试),因为测试目标的visibility无法独立于生产目标进行指定。如果各个测试是在 Bazel 软件包之外引用的,则非测试目标的visibility需要包含依赖于测试的 Bazel 软件包。聚合
test_suite()目标中不包含存在哪些测试以及这些测试的名称。在添加、重命名或移除测试时,包含汇总
test_suite()目标的软件包无需获得OWNERS批准。与在树中其他位置的一个(或多个)
test_suite()目标相比,在同一BUILD.bazel文件中,有人更有可能记得更新test_suite()目标。在某些情况下,可以通过指定空的
tests属性来自动处理测试包含(请参阅上文中的在各个软件包中定义测试和测试套件)。