Cobertura responde quais linhas e caminhos foram executados pela suíte. Ela encontra pontos esquecidos, mas não mede a qualidade das asserções nem prova ausência de defeitos.

Medir linhas e branches

python -m pip install coverage pytest
coverage run --branch -m pytest
coverage report -m
coverage html

Abra htmlcov/index.html e examine linhas ausentes. Branch coverage revela decisões nas quais apenas um destino foi exercitado, algo que a cobertura de linhas pode esconder.

Centralize opções em .coveragerc:

[run]
branch = True
source = src

[report]
show_missing = True
fail_under = 85

O limite deve impedir queda acidental, não incentivar testes sem valor. Exclua código somente quando houver justificativa verificável. O guia de pytest mostra como escrever cenários, e o artigo sobre fixtures e mocks ajuda a isolar dependências.

Interpretar o relatório

Priorize regras de negócio, tratamento de erros e fronteiras externas. Uma linha trivial coberta pesa igual a uma decisão financeira no percentual, mas não no risco. Revise também testes que executam o código sem afirmar o resultado.

Na CI, gere um formato legível pela plataforma e falhe abaixo do limite acordado. Não publique relatórios com caminhos, variáveis ou artefatos sensíveis.

A documentação oficial do coverage.py, consultada em 22 de julho de 2026, detalha cobertura de statements, branches e formatos de relatório. Use o número como mapa de investigação, não como objetivo isolado.

Entender o ciclo medir, combinar e relatar

coverage.py separa a execução do código da apresentação dos resultados. coverage run coleta dados e grava o arquivo .coverage; coverage report produz um resumo no terminal; coverage html cria uma navegação detalhada; coverage xml e coverage json atendem integrações automatizadas. Essa separação permite gerar vários relatórios sem repetir toda a suíte.

Sempre execute a medição a partir de um diretório previsível. Se partes da suíte rodam em processos ou tarefas diferentes, cada execução pode gerar dados próprios, que devem ser combinados antes do relatório final:

coverage erase
coverage run --parallel-mode -m pytest tests/unit
coverage run --parallel-mode -m pytest tests/integration
coverage combine
coverage report -m

coverage erase evita misturar o resultado atual com dados antigos. O modo paralelo cria arquivos distintos e coverage combine os reúne. Em CI distribuída, baixe os artefatos de todas as tarefas para um diretório comum antes de combinar. Só compare porcentagens produzidas com a mesma configuração e o mesmo conjunto de arquivos.

Preferir branch coverage para decisões

Cobertura de linhas registra se uma linha foi executada, mas pode esconder um caminho ausente. Considere:

def calcular_frete(total: float, expresso: bool) -> float:
    if total >= 200 or expresso:
        return 0
    return 18

Um teste com total=250 executa o retorno gratuito, mas não demonstra que expresso=True funciona com um total menor. Outro teste precisa exercitar o retorno pago. Branch coverage observa as transições possíveis de uma decisão e indica caminhos parciais no relatório HTML.

Isso não significa criar um teste para cada combinação mecânica. Escolha casos que representem comportamentos: limite abaixo de 200, exatamente 200, entrega expressa e entrada inválida quando aplicável. As asserções devem conferir o valor devolvido e efeitos relevantes, não apenas chamar a função.

Configurar o escopo corretamente

O argumento source faz coverage.py acompanhar também módulos que poderiam ter sido executados, o que ajuda a listar arquivos totalmente esquecidos. Ajuste o caminho ao layout real:

[run]
branch = True
source =
    src
omit =
    */tests/*
    */migrations/*

[report]
show_missing = True
skip_covered = False
precision = 1
fail_under = 85

[html]
directory = htmlcov

Não copie exclusões sem analisar o projeto. Migrações geradas podem ser excluídas, enquanto uma migração escrita à mão e com transformação crítica talvez mereça teste. Arquivos de teste costumam ficar fora da métrica do código de produção, mas helpers complexos usados pela suíte podem exigir verificação própria.

O contexto de execução também importa. Rodar a suíte a partir de diretórios diferentes pode produzir caminhos que não combinam. Padronize o comando local e o da CI e mantenha .coveragerc, pyproject.toml ou setup.cfg versionado como fonte única da configuração.

Usar exclusões com parcimônia

Algumas linhas são defensivas ou específicas de plataforma. coverage.py reconhece comentários como # pragma: no cover, mas a exceção deve ser revisável:

if TYPE_CHECKING:
    from pacote_externo import Cliente

if __name__ == "__main__":  # pragma: no cover
    main()

Antes de excluir, pergunte se o comportamento pode ser testado por uma interface pública. Não marque blocos difíceis apenas para melhorar o painel. Uma exclusão ampla reduz a capacidade do relatório de revelar riscos futuros. Regras de exclusão por expressão regular também precisam ser específicas e documentadas.

Definir uma política de limite útil

Não existe porcentagem universal. Um projeto novo pode começar com um limite elevado; uma base antiga talvez precise registrar o valor atual e impedir queda enquanto amplia áreas críticas. O fail_under global é simples, porém não impede que código novo sem testes seja compensado por módulos antigos bem cobertos.

Em revisões, observe o diff e pergunte quais caminhos novos foram introduzidos. Uma função de autenticação com 90% de cobertura pode ser mais arriscada do que um módulo de formatação com 60%. Combine o limite automático com revisão de cenários, testes de regressão para defeitos encontrados e análise das linhas ausentes.

Evite arredondamento surpreendente. precision muda a exibição, e o limite usa o valor medido. Defina ambos conscientemente e execute o mesmo comando que a CI antes de enviar uma alteração.

Integrar com pytest e subprocessos

coverage.py pode iniciar pytest diretamente com coverage run -m pytest, o que mantém a ferramenta de medição explícita. O plugin pytest-cov oferece opções integradas ao pytest, mas não é obrigatório. Escolha uma abordagem e documente o comando oficial para que a equipe não gere números diferentes.

Código executado em subprocessos exige configuração adicional, pois um novo interpretador não herda automaticamente a medição. Consulte o suporte de subprocessos da versão usada e confirme que os arquivos paralelos foram salvos e combinados. Não presuma que o processo pai representa workers, filas ou comandos invocados externamente.

Ler o relatório como ferramenta de diagnóstico

Comece pelas linhas ausentes e parciais em módulos de maior risco. Em tratamento de erros, verifique falha de rede, dados inválidos, permissões e limpeza de recursos. Em loops, teste coleções vazias e interrupções. Em condições, procure operandos curtos que impeçam a avaliação de parte da expressão.

Uma melhoria real de cobertura deve vir acompanhada de uma asserção capaz de falhar quando o comportamento quebra. Para avaliar isso, altere mentalmente ou temporariamente o resultado esperado: o teste detectaria a regressão? Técnicas como mutation testing podem aprofundar essa análise, mas não substituem a leitura dos casos.

Na CI, publique o resumo necessário para revisão e restrinja o acesso ao HTML quando o código não for público. Não inclua arquivos de configuração com segredos nem faça upload indiscriminado do diretório de trabalho. O artefato útil é o relatório, não todo o ambiente de execução.