Como Usar o PyTest para Testes de Unidade em Python

Quando você está escrevendo código em Python, é importante garantir que seu código funcione como esperado. Uma das melhores maneiras de fazer isso é utilizando testes unitários, que ajudam a verificar se pequenas partes (ou unidades) do seu código estão funcionando corretamente.

Neste artigo, aprenderemos como escrever e executar testes unitários eficazes em Python usando PyTest, um dos frameworks de teste mais populares para Python.

O que são Testes Unitários?

Testes unitários são pequenos e simples testes que se concentram em verificar uma única função ou um pequeno trecho de código. Eles ajudam a garantir que seu código funcione como esperado e podem detectar bugs precocemente.

Testes unitários podem ser escritos para diferentes partes do seu código, como funções, métodos e até classes. Ao escrever testes unitários, você pode testar seu código sem executar o programa inteiro.

Por que Usar PyTest?

PyTest é um framework de teste popular para Python que facilita escrever e executar testes.

É simples de usar e possui muitos recursos úteis, como:

  • Permite que você escreva casos de teste simples e claros.
  • Fornece recursos avançados como fixtures, testes parametrizados e plugins.
  • Funciona bem com outras ferramentas e bibliotecas de teste.
  • Gera resultados e relatórios de teste fáceis de ler.

Configurando o PyTest no Linux

Antes de começarmos a escrever testes, precisamos instalar PyTest. Se você não tiver PyTest instalado, pode instalá-lo usando o gerenciador de pacotes Python chamado pip.

pip install pytest

Uma vez que o PyTest esteja instalado, você está pronto para começar a escrever testes!

Escrevendo Seu Primeiro Teste com PyTest

Vamos começar escrevendo uma função simples e, em seguida, escrever um teste para ela.

Passo 1: Escreva uma Função Simples

Primeiro, vamos criar uma função Python que queremos testar. Vamos supor que temos uma função que soma dois números:

# add.py
def add(a, b):
    return a + b

Esta é uma função simples que recebe dois números a e b, soma-os e retorna o resultado.

Passo 2: Escreva um Teste para a Função

Agora, vamos escrever um teste para a função de adição. No PyTest, os testes são escritos em arquivos separados, tipicamente nomeados test_*.py para facilitar a identificação dos arquivos de teste.

Criar um novo arquivo chamado test_add.py e escreva o seguinte código de teste:

# test_add.py
from add import add

def test_add_numbers():
    assert add(2, 3) == 5
    assert add(-1, 1) == 0
    assert add(0, 0) == 0

Explicação do código acima:

  • Importamos a função add do arquivo add.py.
  • Definimos uma função de teste chamada test_add_numbers(). No PyTest, uma função de teste deve começar com a palavra test_.
  • Dentro da função de teste, usamos a declaração assert para verificar se o resultado da chamada da função add corresponde ao valor esperado. Se a condição na declaração assert for True, o teste passa; caso contrário, falha.

Passo 3: Execute o Teste

Para executar o teste, abra seu terminal e navegue até o diretório onde seu arquivo test_add.py está localizado e, em seguida, execute o seguinte comando:

pytest

O PyTest encontrará automaticamente todos os arquivos de teste (aqueles que começam com test_) e executará os testes dentro deles. Se tudo estiver funcionando corretamente, você deverá ver uma saída como esta:

Verifying Python Code Functionality

O ponto (.) indica que o teste passou. Se houver algum problema, PyTest mostrará uma mensagem de erro.

Escrevendo Testes Mais Avançados

Agora que sabemos como escrever e executar um teste básico, vamos explorar alguns recursos mais avançados do PyTest.

Testando Exceções Esperadas

Às vezes, você quer testar se seu código levanta as exceções corretas quando algo dá errado. Você pode fazer isso com a função pytest.raises().

Vamos supor que queremos testar uma função que divide dois números. Queremos levantar uma exceção se o segundo número for zero (para evitar erros de divisão por zero).

Aqui está a função divide:

# divide.py
def divide(a, b):
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

Agora, vamos escrever um teste para essa função que verifica se o ValueError é levantado quando tentamos dividir por zero:

# test_divide.py
from divide import divide
import pytest

def test_divide_numbers():
    assert divide(10, 2) == 5
    assert divide(-10, 2) == -5
    assert divide(10, -2) == -5

def test_divide_by_zero():
    with pytest.raises(ValueError):
        divide(10, 0)

Explicação do código:

  • Adicionamos uma nova função de teste chamada test_divide_by_zero().
  • Dentro desta função, usamos pytest.raises(ValueError) para verificar se um ValueError é gerado quando chamamos a função de divisão com zero como o segundo argumento.

Execute os testes novamente com o comando pytest. Se tudo estiver funcionando corretamente, você deve ver esta saída:

Test Your Code with PyTest

Usando Fixtures para Configuração e Limpeza

Em alguns casos, você pode precisar configurar certas condições antes de executar seus testes ou limpar após os testes serem concluídos. PyTest fornece fixtures para lidar com isso.

Uma fixture é uma função que você pode usar para configurar ou desmontar condições para seus testes. Fixtures são frequentemente usadas para criar objetos ou conectar a bancos de dados que são necessários para os testes.

Aqui está um exemplo de como usar uma fixture para configurar um diretório temporário para testar operações de arquivo:

# test_file_operations.py
import pytest
import os

@pytest.fixture
def temporary_directory():
    temp_dir = "temp_dir"
    os.mkdir(temp_dir)
    yield temp_dir  # This is where the test will run
    os.rmdir(temp_dir)  # Cleanup after the test

def test_create_file(temporary_directory):
    file_path = os.path.join(temporary_directory, "test_file.txt")
    with open(file_path, "w") as f:
        f.write("Hello, world!")
    
    assert os.path.exists(file_path)

Explicação do código:

  • Definimos uma fixture chamada temporary_directory que cria um diretório temporário antes do teste e o exclui depois.
  • A função de teste test_create_file() usa esta fixture para criar um arquivo no diretório temporário e verifica se o arquivo existe.

Execute os testes novamente com o comando pytest. O PyTest detectará automaticamente e usará a fixture.

Parametrize Seus Testes com Pytest

Às vezes, você quer executar o mesmo teste com diferentes entradas. PyTest permite que você faça isso facilmente usando parametrize.

Vamos supor que queremos testar nossa função add com vários pares de números. Em vez de escrever funções de teste separadas para cada par, podemos usar pytest.mark.parametrize para executar o mesmo teste com entradas diferentes.

# test_add.py
import pytest
from add import add

@pytest.mark.parametrize("a, b, expected", [
    (2, 3, 5),
    (-1, 1, 0),
    (0, 0, 0),
    (100, 200, 300)
])
def test_add_numbers(a, b, expected):
    assert add(a, b) == expected

Explicação do código:

  • Usamos o decorador pytest.mark.parametrize para definir múltiplos conjuntos de entradas (a, b e expected).
  • O teste function test_add_numbers() será executado uma vez para cada conjunto de entradas.

Execute os testes novamente com o comando pytest, que rodará o teste quatro vezes, uma para cada conjunto de entradas.

Conclusão

Neste artigo, aprendemos como escrever e executar testes unitários eficazes em Python usando PyTest para identificar bugs cedo e garantir que seu código funcione conforme o esperado.

PyTest facilita a escrita e a execução desses testes, e com seus recursos poderosos, você pode lidar com necessidades de teste mais complexas à medida que avança em sua jornada no Python.

Source:
https://www.tecmint.com/unit-testing-python-code-with-pytest/