O Bazel inclui um subcomando coverage
para produzir relatórios de cobertura
de código em repositórios que podem ser testados com bazel coverage
. Devido
às idiossincrasias dos vários ecossistemas de linguagem, nem sempre é
trivial fazer isso funcionar para um determinado projeto.
Esta página documenta o processo geral de criação e visualização de relatórios de cobertura e também apresenta algumas notas específicas para idiomas com configuração conhecida. É melhor ler primeiro a seção geral e depois ler sobre os requisitos de um idioma específico. Observe também a seção de execução remota, que requer algumas considerações adicionais.
Embora muitas opções de personalização sejam possíveis, este documento se concentra na
produção e consumo de relatórios lcov
, que atualmente é a
rota mais aceita.
Como criar um relatório de cobertura
Preparação
O fluxo de trabalho básico para criar relatórios de cobertura requer o seguinte:
- Um repositório básico com destinos de teste
- Uma cadeia de ferramentas com as ferramentas de cobertura de código específicas da linguagem instaladas
- Uma configuração de "instrumentação" correta
Os dois primeiros são específicos de linguagem e, na maioria das vezes, diretos, mas o segundo pode ser mais difícil para projetos complexos.
"Instrumentação", neste caso, se refere às ferramentas de cobertura que são
usadas para um destino específico. O Bazel permite ativar isso para um
subconjunto específico de arquivos usando a flag
--instrumentation_filter
,
que especifica um filtro para destinos que são testados com a
instrumentação ativada. Para ativar a instrumentação para testes, a flag
--instrument_test_targets
é necessária.
Por padrão, o Bazel tenta corresponder aos pacotes de destino e imprime o
filtro relevante como uma mensagem INFO
.
Cobertura em execução
Para produzir um relatório de cobertura, use bazel coverage
--combined_report=lcov
[target]
. Isso executa os
testes para o destino, gerando relatórios de cobertura no formato lcov
para cada arquivo.
Depois de concluído, o Bazel executa uma ação que coleta todos os arquivos de cobertura
produzidos e os mescla em um, que é finalmente
criado em $(bazel info
output_path)/_coverage/_coverage_report.dat
.
Os relatórios de cobertura também serão gerados se os testes falharem. No entanto, isso não se estende aos testes com falha, apenas os testes aprovados são informados.
Como conferir a cobertura
O relatório de cobertura só é gerado no formato lcov
, que não é legível por humanos. Assim, podemos usar o utilitário genhtml
(parte do projeto lcov) para gerar um relatório que pode ser visualizado em um navegador da Web:
genhtml --output genhtml "$(bazel info output_path)/_coverage/_coverage_report.dat"
O genhtml
também lê o código-fonte para anotar a cobertura
ausente nesses arquivos. Para que isso funcione, é esperado que
genhtml
seja executado na raiz do projeto do Bazel.
Para ver o resultado, basta abrir o arquivo index.html
produzido no diretório
genhtml
em qualquer navegador da Web.
Para mais ajuda e informações sobre a ferramenta genhtml
ou o formato de cobertura lcov
, consulte o projeto lcov (links em inglês).
Execução remota
No momento, a execução com a execução de teste remota tem algumas ressalvas:
- Ainda não é possível executar remotamente a ação de combinação de relatórios. Isso ocorre
porque o Bazel não considera os arquivos de saída de cobertura como parte do
gráfico (consulte este problema) e, portanto,
não pode tratá-los corretamente como entradas para a ação de combinação. Para
contornar isso, use
--strategy=CoverageReport=local
.- Observação: pode ser necessário especificar algo como
--strategy=CoverageReport=local,remote
se o Bazel estiver configurado para tentarlocal,remote
, devido à forma como ele resolve estratégias.
- Observação: pode ser necessário especificar algo como
--remote_download_minimal
e sinalizações semelhantes também não podem ser usadas como consequência da primeira.- No momento, o Bazel não vai conseguir criar informações de cobertura se os testes
tiverem sido armazenados em cache anteriormente. Para contornar isso,
--nocache_test_results
pode ser definido especificamente para execuções de cobertura, embora isso tenha um alto custo em termos de tempo de teste. --experimental_split_coverage_postprocessing
e--experimental_fetch_all_coverage_outputs
- Normalmente, a cobertura é executada como parte da ação de teste. Por isso, por padrão, não recebemos toda a cobertura como saídas da execução remota. Essas flags substituem o padrão e recebem os dados de cobertura. Consulte este problema para mais detalhes.
Configuração específica do idioma
Java
O Java deve funcionar com a configuração padrão. Os conjuntos de ferramentas do Bazel contêm tudo o que é necessário para execução remota, incluindo o JUnit.
Python
Pré-requisitos
A execução da cobertura com Python tem alguns pré-requisitos:
- Um binário do Bazel que inclui b01c859 (link em inglês), que precisa ser qualquer Bazel >3.0.
- Uma versão modificada de coverage.py.
Como consumir o coverage.py modificado
Uma maneira de fazer isso é usando rules_python, que permite
o uso de um arquivo requirements.txt
. Os requisitos listados
no arquivo são criados como destinos do Bazel usando a regra de repositório
pip_install.
O requirements.txt
precisa ter a seguinte entrada:
git+https://github.com/ulfjack/coveragepy.git@lcov-support
Os arquivos rules_python
, pip_install
e requirements.txt
devem ser usados no arquivo WORKSPACE como:
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",
)
Assim, o requisito abrang.py pode ser consumido por destinos de teste,
definindo o seguinte nos arquivos BUILD
:
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",
],
)
Se você estiver usando um conjunto de ferramentas hermético do Python, em vez de adicionar a dependência
de cobertura a cada destino py_test
, adicione a ferramenta de cobertura à
configuração do conjunto de ferramentas.
Como a regra pip_install depende da cadeia de ferramentas
do Python, ela não pode ser usada para buscar o módulo coverage
.
Em vez disso, adicione o WORKSPACE
, por exemplo.
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",
],
)
Em seguida, configure a cadeia de ferramentas do Python, por exemplo:
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",
)