Garantir a qualidade do software é fundamental para qualquer aplicação, independente da tecnologia ou de sua funcionalidade. Um software sem qualidade começa apresentar defeitos muito antes do que o imaginado, a manutenção se torna cara e cansativa para o time de desenvolvimento.
Podemos garantir a qualidade do nosso software de diferentes formas, como aplicar padrões de design ou implementar testes automatizados, por exemplo. Com os testes o time fica confiável de fazer alterações em qualquer parte da aplicação sem a preocupação de impactar outras partes do sistema.
Testar uma aplicação manualmente e feita por humanos pode demorar semanas e ainda sim erros podem passar, isso sem falar que enquanto uma parte está sendo testada, novas features estão sendo lançadas. Implementando testes automatizados, não é necessário um humano para tal tarefa e o feedback leva questões de segundos.
A gente já falou por aqui sobre testes automatizados om Pytest, portanto vamos criar agora uma cobertura de código, ou seja, verificar o quanto nossa aplicação tem testes que garantem a qualidade.
A cobertura de código é uma métrica fundamental no desenvolvimento de software, pois quantifica a extensão em que seu código-fonte é testado por meio de testes automatizados. Ao medir a cobertura de código, você obtém uma visão clara de quais partes do seu código foram afetadas por testes e quais permanecem não testadas. Isso é crucial para garantir a qualidade do software, uma vez que ajuda a identificar áreas suscetíveis a erros não detectados. Além disso, a cobertura de código promove a confiabilidade do código, acelerando o processo de desenvolvimento, facilitando a manutenção e promovendo uma cultura de testes sólida. Em resumo, a cobertura de código é uma prática essencial para desenvolvedores que desejam criar software robusto, confiável e de alta qualidade.
Ainda é possível aplicar regras na sua ferramenta de versionamento, como o Github, por exemplo, para que um commit só seja aprovado se o código em questão tiver um bom percentual de cobertura.
O Pytest-cov é uma extensão (plugin) do framework de teste Pytest que é usado para medir a cobertura de código dos testes automatizados em Python. Ele ajuda os desenvolvedores a determinar quais partes do código-fonte de um projeto estão sendo testadas pelos testes e a calcular a porcentagem de cobertura de código.
Algumas das vantagens no uso o Pytest-cov é: Geração de Relatórios de cobertura, Integração com o Pytest, Integração com ferramentas de CI/CD, excluir arquivos e blocos que não devem ser testados.
A instalação do Pytst-cov é bem simples e requer apenas um único comando, para isso na raiz do seu projeto rode o comando abaixo.
pip install pytest-cov
Ou se preferir, crie um arquivo na raiz do seu projeto chamada de requirements.txt
e insira o comando pytest-cov
. Agora rode o comando pip abaixo para instalar as bibliotecas listadas no arquivo.
pip install -r requirements.txt
Agora rode o comando pip abaixo para garantir que o Pytest-cov foi instalado corretamente.
pip show pytest-cov
#pip show pytest-cov | grep Version
O Pytest-cov já implementa o Pytest e adiciona novos recursos para cobertura de código, sendo assim devemos escrever nossos testes normalmente como se estivéssemos usando apenas o Pytest.
Considere a classe de matemática abaixo que está no diretório src/app/math.py
.
class Math:
def sum(self, num1, num2):
return num1 + num2
def mult(self, num1, num2): # pragma: no cover
return num1 * num2
Agora observe nosso caso de teste que está no diretório tests/app/test_math.py
.
from src.app.math import Math
def test_sum():
math = Math()
assert math.sum(1, 1) == 2
Para executar nosso teste basta rodar o comando abaixo no seu terminal.
pytest
Com nossos testes devidamente escritos e bibliotecas instaladas, agora vamos testar a nossa cobertura de código, para isso vamos rodar o comando abaixo no terminal.
pytest --cov=src/ --cov-report=html --cov-report term-missing
A saída será algo como mostra a imagem a seguir.
Os comandos acima podem parecer um pouco confuso, então vamos entender o que ele faz:
pytest
- Inicializa a ferramenta de testes;--cov=src/
- Define qual é a pasta do nosso projeto, no caso a pasta src/
;--cov-report=html
- Aqui definimos como queremos o nosso relatório, ele vai ser em HTML e estará na pasta htmlcov. Caso queira mudar a pasta basta alterar para --cov-report=html:minha_pasta
;--cov-report term-missing
- Exibe o resultado do teste no terminal.Vamos concordar que o comando acima não é nem um pouco intuitivo, são muitas as configurações que podemos definir, então vamos minimizar Início do Narrador isso. Na raiz do seu projeto crie um arquivo chamado pytest.ini
e insira o comando abaixo.
[pytest]
addopts = --cov=src/ --cov-report=html --cov-report term-missing
Agora é só a gente rodar o comando pytest
no terminal que todas as configurações acima serão levadas em consideração.
Agora nós temos dois problemas, se você analisar a imagem acima na qual rodamos os nossos testes de cobertura, vai notar que o total de cobertura é de 50% devido a uma função não testada e que os arquivos index.py
e __init__.py
estão no relatório mesmo não contendo nada para ser testado.
É comum a gente ter arquivos de configuração, inicializadores, dentre outros que não fazem parte do código a ser testado. Eles podem impactar diretamente na nota de cobertura quanto constar no relatório.
Vamos remover alguns arquivos, para isso na raiz do seu projeto crie uma pasta chamada .coveragerc
e insira o código abaixo.
[run]
source = src/
omit = __init__.py, *index.py
Perceba que na seção omit
a gente insere e separa por vírgula os arquivos que desejamos remover da cobertura. Agora rodando o mesmo teste vamos ter um resultado diferente.
Se você analisar nossa métrica, estamos com 80% de cobertura de código, isso devido a nossa função mult()
do arquivo src\app\math.py não ter uma cobertura de testes. Pode acontecer da gente ter um bloco de código que precisa ser ignorado, como um método que está em construção.
Para remover um bloco de código, basta inserir o comando # pragma: no cover
logo acima dele. Para o nosso exemplo, vamos ignorar a nossa função mult()
, portanto devemos colocar logo a frente da declaração da nossa função, conforme mostra o código abaixo.
def mult(self, num1, num2): # pragma: no cover
return num1 * num2
Agora se a gente rodar nossos testes vamos ver que a cobertura ficou em 100%.
Algumas boas práticas devem ser levadas em consideração durante a escrita e cobertura de código, são elas:
Confira a nossa videoaula complementar para aprender mais sobre cobertura de testes com Pytest-cov.