Bazel 使用 coverage
子指令,針對可透過 bazel coverage
進行測試的存放區產生程式碼涵蓋率報表。由於各種語言生態系統的慣用語同步,為特定專案執行這項工作並不容易。
本頁面說明建立及查看涵蓋率報表的一般程序,並針對設定已知的語言提供一些特定語言的附註。建議您先閱讀一般說明,再查看特定語言的規定。請注意遠端執行區段,這需要一些額外考量。
雖然可以進行許多自訂設定,但這份文件著重於產生及使用 lcov
報表,這是目前最受支援的路徑。
建立涵蓋率報表
準備
建立涵蓋率報表的基本工作流程需要符合下列條件:
- 含有測試目標的基本存放區
- 已安裝特定語言程式碼涵蓋率工具的工具鍊
- 正確的「檢測」設定
前兩種方法都與語言相關,且大多相當簡單,但對於複雜專案而言,後者可能較為困難。
此處的「檢測」是指用於特定目標的涵蓋範圍工具。Bazel 可讓您使用 --instrumentation_filter
標記,為特定檔案子集啟用這項功能,該標記會為啟用檢測功能的目標指定篩選器。如要啟用測試的檢測功能,您必須提供 --instrument_test_targets
標記。
根據預設,bazel 會嘗試比對目標套件,並將相關篩選器顯示為 INFO
訊息。
執行涵蓋率
如要產生涵蓋率報表,請使用 bazel coverage
--combined_report=lcov
[target]
。這會為目標執行測試,並為每個檔案以 lcov 格式產生涵蓋率報告。
完成後,bazel 會執行動作來收集所有產生的涵蓋率檔案,並合併成一個檔案,最後在 $(bazel info
output_path)/_coverage/_coverage_report.dat
下建立此動作。
如果測試失敗,系統也會產生涵蓋率報表,但請注意,這並不涵蓋失敗的測試,只會回報通過的測試。
查看涵蓋率
涵蓋率報表只會以人類無法讀取的 lcov
格式輸出。從這裡,我們可以使用 genhtml
公用程式 (lcov 專案的一部分) 產生可在網頁瀏覽器中查看的報表:
genhtml --output genhtml "$(bazel info output_path)/_coverage/_coverage_report.dat"
請注意,genhtml
也會讀取原始碼,為這些檔案缺少涵蓋範圍加上註解。為此,genhtml
應在 bazel 專案的根目錄中執行。
如要查看結果,只要在任何網路瀏覽器中開啟 genhtml
目錄中產生的 index.html
檔案即可。
如需 genhtml
工具或 lcov
涵蓋範圍格式的進一步協助和資訊,請參閱 lcov 專案。
遠端執行
透過遠端測試執行作業執行目前有一些注意事項:
- 報表組合動作目前無法遠端執行。這是因為 Bazel 不會將涵蓋率輸出檔案視為圖表的一部分 (請參閱這個問題),因此無法將其正確視為組合動作的輸入檔案。如要解決這個問題,請使用
--strategy=CoverageReport=local
。- 注意:如果 Bazel 已設為嘗試
local,remote
,則可能需要改為指定--strategy=CoverageReport=local,remote
,因為 Bazel 會以這種方式解析策略。
- 注意:如果 Bazel 已設為嘗試
--remote_download_minimal
和類似的旗標也無法使用,因為這是前者的後果。- 如果先前已快取測試,Bazel 目前就無法建立涵蓋率資訊。如要解決這個問題,您可以專門針對涵蓋率執行設定
--nocache_test_results
,不過這當然在測試時間會產生相當高的成本。 --experimental_split_coverage_postprocessing
和--experimental_fetch_all_coverage_outputs
- 通常涵蓋率會在測試動作中執行,因此預設情況下,我們不會將所有涵蓋率做為遠端執行作業的輸出內容。這些標記會覆寫預設值,並取得涵蓋率資料。詳情請參閱這個問題。
特定語言設定
Java
Java 應採用預設設定,立即運作。bazel 工具鏈包含遠端執行作業所需的所有內容,包括 JUnit。
Python
必要條件
使用 Python 執行涵蓋範圍,需要滿足一些先決條件:
- 包含 b01c859 的 bazel 二進位檔,應該使用的任何 Bazel >3.0。
- 已修改的 Coverage.py 版本。
使用經過修改的 coverage.py
其中一種方法是透過 rules_python 使用 requirements.txt
檔案,隨後系統會使用 pip_install 存放區規則,將檔案中所列的需求建立為 bazel 目標。
requirements.txt
應包含以下項目:
git+https://github.com/ulfjack/coveragepy.git@lcov-support
接著,您應該在 WORKSPACE 檔案中使用 rules_python
、pip_install
和 requirements.txt
檔案,如下所示:
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "rules_python",
url = "https://github.com/bazelbuild/rules_python/releases/download/0.5.0/rules_python-0.5.0.tar.gz",
sha256 = "cd6730ed53a002c56ce4e2f396ba3b3be262fd7cb68339f0377a45e8227fe332",
)
load("@rules_python//python:pip.bzl", "pip_install")
pip_install(
name = "python_deps",
requirements = "//:requirements.txt",
)
接著,只要在 BUILD
檔案中設定下列內容,測試目標就能使用 coverage.py 需求:
load("@python_deps//:requirements.bzl", "entry_point")
alias(
name = "python_coverage_tools",
actual = entry_point("coverage"),
)
py_test(
name = "test",
srcs = ["test.py"],
env = {
"PYTHON_COVERAGE": "$(location :python_coverage_tools)",
},
deps = [
":main",
":python_coverage_tools",
],
)
如果您使用密封的 Python 工具鏈,請改為將涵蓋率工具新增至工具鏈設定,而非將涵蓋率依附元件新增至每個 py_test
目標。
由於 pip_install 規則依附 Python 工具鏈,因此無法用於擷取 coverage
模組。請改為在 WORKSPACE
中加入,例如
http_archive(
name = "coverage_linux_x86_64"",
build_file_content = """
py_library(
name = "coverage",
srcs = ["coverage/__main__.py"],
data = glob(["coverage/*", "coverage/**/*.py"]),
visibility = ["//visibility:public"],
)
""",
sha256 = "84631e81dd053e8a0d4967cedab6db94345f1c36107c71698f746cb2636c63e3",
type = "zip",
urls = [
"https://files.pythonhosted.org/packages/74/0d/0f3c522312fd27c32e1abe2fb5c323b583a5c108daf2c26d6e8dfdd5a105/coverage-6.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
],
)
然後將 Python 工具鍊設為,例如:
py_runtime(
name = "py3_runtime_linux_x86_64",
coverage_tool = "@coverage_linux_x86_64//:coverage",
files = ["@python3_9_x86_64-unknown-linux-gnu//:files"],
interpreter = "@python3_9_x86_64-unknown-linux-gnu//:bin/python3",
python_version = "PY3",
)
py_runtime_pair(
name = "python_runtimes_linux_x86_64",
py2_runtime = None,
py3_runtime = ":py3_runtime_linux_x86_64",
)
toolchain(
name = "python_toolchain_linux_x86_64",
exec_compatible_with = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
],
toolchain = ":python_runtimes_linux_x86_64",
toolchain_type = "@bazel_tools//tools/python:toolchain_type",
)