測試 Rust 程式碼

本文說明編寫 Rust 程式碼測試的最佳做法。如需定義及執行測試套件和元件的操作說明,請參閱元件測試指南。

本文適用於在 fuchsia.git 內工作的開發人員,所述工作流程可能不適用於 IDK 消費者。

本教學課程的原始碼位於 //examples/hello_world/rust。

單元測試

在程式碼中新增測試

在 Fuchsia 內新增 Rust 單元測試的慣用方式,與在外部新增的方式相同,只要將下列程式碼片段放入要編寫的任何測試底部,即可輕鬆完成:

#[cfg(test)]
mod tests {
    #[fuchsia::test]
    fn it_works() {
        assert_eq!(true, true);
    }
}

這麼做會建立名為 tests 的新功能模組,且這個功能模組只會在建構單元測試時納入。使用 #[fuchsia::test] 註解的任何函式都會做為測試執行,如果函式順利傳回,測試就會通過。

#[fuchsia::test] 也支援測試非同步程式碼。

#[fuchsia::test]
async fn my_test() {
    let some_future = async { 4 };
    assert_eq!(some_future.await, 4);
}

一般的 #[test] 註解也適用,但它不支援非同步測試,也不支援開箱即用的記錄功能。如果您認為 Crate 可能會在 Fuchsia 程式碼集以外的地方使用,建議使用 #[test]。

建立測試

單元測試可由 Rust 目標自動建構 (即 rustc_binary 或 rustc_library)。這兩種方法大致相似。

為 Rust 二進位檔建構測試

如果您要測試 Rust 二進位檔 (也就是有 main.rs),這個部分會很有幫助。如果您要測試的是程式庫,請參閱下一節。

您的 BUILD.gn 檔案必須先匯入 rustc_binary 範本,才能使用:

import("//build/rust/rustc_binary.gni")

只有在新增 with_unit_tests = true 設定時,rustc_binary GN 範本才會建構單元測試:

rustc_binary("bin") {
  name = "hello_world_rust"

  # Generates the "bin_test" build target
  with_unit_tests = true
  edition = "2024"

  deps = []
  test_deps = [ "//src/lib/fuchsia" ]

  sources = [ "src/main.rs" ]
}

設定 with_unit_tests = true 會導致這項建構規則產生兩個不同的可執行檔,一個使用提供的名稱,另一個則在提供的名稱後方附加 _bin_test。

以這個範例來說,建立的可執行檔名稱為:

  • hello_world_rust;和
  • hello_world_rust_bin_test。

為 Rust 程式庫建構測試

首先,您的 BUILD.gn 檔案需要匯入 rustc_library 範本,才能提供該範本:

import("//build/rust/rustc_library.gni")

只有在新增 with_unit_tests = true 設定時,rustc_library GN 範本才會建構單元測試,與上述 rustc_binary 的情況類似。

不過,在此情況下,系統會建立名稱不同的測試二進位檔:

  • hello_world_rust_lib_test。請注意,二進位檔的名稱與程式庫產生的名稱不同。

二進位檔名稱很重要,因為後續步驟會用到。

將單元測試限制在主機或 Fuchsia 上執行

根據預設,with_unit_tests = true 會在評估 BUILD.gn 檔案的每個工具鍊中定義 <target_name>_test 目標。如要只為部分平台定義,請將 with_unit_tests 設為下列其中一個字串,這些字串與 Bazel Rust 巨集的介面相符:

  • "host":只在主機工具鍊中定義測試目標。
  • "fuchsia":僅在 Fuchsia 工具鍊中定義測試目標。
  • "both":在主機和 Fuchsia 工具鍊中定義測試目標。

舉例來說,如要只在主機上建構及執行程式庫的單元測試:

rustc_library("lib") {
  name = "my_lib"
  edition = "2024"
  with_unit_tests = "host"
  sources = [ "src/lib.rs" ]
}

group("tests") {
  testonly = true
  deps = [ ":lib_test($host_toolchain)" ]
}

封裝及執行測試

如要執行先前目標產生的測試,請將測試封裝為測試元件。測試元件包含元件資訊清單,其中宣告測試所需的各項功能。

您的 BUILD.gn 檔案需要匯入元件範本:

import("//build/components.gni")

您可以使用下列建構規則套件測試:

  • fuchsia_test_package():套件範本,可將多個測試元件及其依附元件收集到單一套件中。測試套件通常用於整合測試。
  • fuchsia_unittest_package():專為含有單元測試的套件設計的範本。單元測試套件可為沒有必要功能的單元測試產生最少的元件資訊清單。

以 Hello World 二進位檔範例來說,單元測試套件會參照產生的目標 bin_test (根據目標名稱 bin 和隱含後置字元 _test) 和 hello_world_rust_bin_test (根據 name 節的值)。

fuchsia_unittest_package("hello-world-rust-tests") {
  deps = [ ":bin_test" ]
}

如要執行測試,請執行:

fx test hello-world-rust-tests

如要瞭解如何封裝及執行測試,請參閱「測試元件」和「建構元件」。

實用的 Crate

下列樹狀結構內第三方 Crate 可協助您編寫測試:

  • assert_matches:提供巨集 assert_matches!,讓模式斷言符合人體工學。
  • pretty_assertions:提供替代的 assert_eq! 巨集,可在判斷結果為失敗時顯示彩色差異。

這些項目會顯示在 BUILD.gn 的 test_deps 下。

rustc_binary("bin") {
  name = "my_test"
  with_unit_tests = true
  edition = "2024"

  test_deps = [
    "//third_party/rust_crates:matches",
    "//third_party/rust_crates:pretty_assertions",
  ]
}