測試和測試套件

總覽

本頁面提供樣式和最佳做法,說明如何在 BUILD.bazel 檔案中定義及建構測試和 test_suite() 目標,並將其連結至 GN 建構圖。除了常見的 Bazel 樣式指南和最佳做法,以及 BUILD.bazel 檔案樣式指南外,本頁面也提供相關指引。如要瞭解如何定義、建構及執行主機和裝置測試目標,請參閱「Bazel 測試」。

本頁面屬於 Bazel 樣式指南和最佳做法的一部分。

測試套件結構

在 Fuchsia 的 Bazel 建構圖中,測試會分成三個層級,並連結至建構作業:

  1. 個別套件:每個 BUILD.bazel 檔案都會定義私人 *_test 目標,並透過名為 "tests" 和/或 "host_tests" 的套件 test_suite() 目標公開這些目標 (請參閱「在個別套件中定義測試和測試套件」)。

  2. 匯總套件:專用於某個區域或子系統群組的套件,可將個別套件中的 test_suite() 目標匯總至匯總 test_suite() 目標 (例如 "bluetooth_tests")。

  3. GN 建構圖整合:雖然 GN 會驅動整體建構作業,但 BUILD.gn 中的 bazel_test_suite() 目標會參照同一目錄中的匯總 test_suite() 目標,因此測試會包含在 tests.json 中 (請參閱「將 Bazel 測試套件連結至 GN 建構圖」)。

個別測試「不得」在定義測試的套件之外參照,且套件不會遞迴包含子套件中的測試或 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" 結尾。Bazel test_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 程式庫單元測試。在這類情況下,請向建構團隊尋求指引。(累積一些這類情境的經驗後,即可更新這段文字)。

在個別套件中定義測試和測試套件

公開套件 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" 的 test_suite(),且具有空白 tests 屬性 (tests = [])。每個這類 test_suite() 都應從適當的匯總 test_suite() 目標參照。

這樣一來,日後新增至套件的任何測試 (無論是明確新增或透過巨集新增),都會納入建構圖。如果能強制執行這項操作,也能避免忘記將測試新增至建構圖。

主要例外狀況是同時包含 Fuchsia 和主機測試的套件,因為目前無法在兩種情況下使用相同的測試目標,因此無法使用空白 tests 屬性機制。

修改測試套件(),並將 tests 屬性留空

修改現有 test_suite() 時,如果其 tests 屬性為空白或未指定,您必須確保套件定義的所有測試都已明確新增至套件中的這個或其他 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 建構圖

使用 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 核准。

  • 相較於樹狀結構中其他 位置的一或多個 檔案,使用者更有可能記得更新同一個 BUILD.bazel 檔案中的 test_suite() 目標。

  • 在某些情況下,您可以指定空白的 tests 屬性,自動處理測試納入作業 (請參閱上方的「在個別套件中定義測試和測試套件」)。