Testes Avançados em Python: unittest, pytest e Mocks Complexos

Testes são essenciais para garantir qualidade, confiabilidade e manutenção de sistemas Python. Técnicas avançadas vão além de simples asserts, envolvendo testes unitários, integração, mocks complexos, parametrização e fixtures avançadas.


1. Fundamentos de Testes

  • Testes Unitários: verificam funcionalidades isoladas de funções ou classes.

  • Testes de Integração: verificam como múltiplos componentes interagem.

  • Mocks: substituem dependências externas para testar unidades isoladas.

Python oferece módulos nativos (unittestunittest.mock) e frameworks avançados como pytest.


2. unittest Avançado

2.1 Estrutura Básica

import unittest

class TestMath(unittest.TestCase):
    def test_soma(self):
        self.assertEqual(2 + 2, 4)
  • Herda unittest.TestCase.

  • Usa métodos assertEqualassertTrueassertRaises, etc.

2.2 Fixtures e Setup/Teardown

class TestDB(unittest.TestCase):
    def setUp(self):
        self.db = criar_conexao_mock()

    def tearDown(self):
        self.db.fechar()

    def test_query(self):
        resultado = self.db.consultar("SELECT 1")
        self.assertEqual(resultado, 1)
  • setUp executa antes de cada teste

  • tearDown limpa recursos após o teste

2.3 Subtests

  • Permite rodar vários cenários dentro do mesmo teste:

def test_multiplos_valores(self):
    for valor in range(5):
        with self.subTest(valor=valor):
            self.assertLess(valor, 5)
  • Facilita identificação de falhas por caso específico.


3. pytest Avançado

3.1 Sintaxe Simples

def soma(a, b):
    return a + b

def test_soma():
    assert soma(2, 3) == 5
  • pytest permite asserts nativos, sem precisar de TestCase.

3.2 Parametrização

import pytest

@pytest.mark.parametrize("a,b,resultado", [(2,3,5),(0,0,0),(-1,1,0)])
def test_soma_param(a,b,resultado):
    assert a + b == resultado
  • Permite testar múltiplos cenários automaticamente.

3.3 Fixtures Avançadas

import pytest

@pytest.fixture(scope="module")
def db():
    conexao = criar_conexao_mock()
    yield conexao
    conexao.fechar()

def test_query(db):
    resultado = db.consultar("SELECT 1")
    assert resultado == 1
  • scope define duração da fixture (functionmodulesession)

  • yield permite setup/teardown avançado


4. Mocks Complexos

4.1 Mocking de Funções e Objetos

from unittest.mock import patch

@patch("modulo.database.query")
def test_query_mock(mock_query):
    mock_query.return_value = 42
    from modulo import database
    resultado = database.query("SELECT 1")
    assert resultado == 42
  • Substitui função real por mock configurável.

4.2 Side Effects

  • Para simular retornos diferentes em chamadas sequenciais ou exceções:

mock_query.side_effect = [1, 2, Exception("Erro simulado")]

4.3 Mock de Context Managers

from unittest.mock import MagicMock, patch

mock_file = MagicMock()
mock_file.__enter__.return_value.read.return_value = "conteúdo"
with patch("builtins.open", return_value=mock_file):
    with open("arquivo.txt") as f:
        conteudo = f.read()
    assert conteudo == "conteúdo"
  • Permite simular arquivos, conexões e recursos com with.

4.4 Mock de Classes Inteiras

with patch("modulo.ClasseExterna") as MockClass:
    MockClass.return_value.metodo.return_value = 10
  • Útil para dependências externas ou caras de inicializar.


5. Testes Assíncronos

import pytest
import asyncio

async def async_soma(a,b):
    await asyncio.sleep(0.1)
    return a+b

@pytest.mark.asyncio
async def test_async_soma():
    resultado = await async_soma(2,3)
    assert resultado == 5

6. Boas Práticas Profissionais

  1. Organize testes em módulos e pacotes separados (tests/).

  2. Use pytest para simplicidade, parametrização e fixtures.

  3. Use mocks e side effects para isolar testes e simular falhas.

  4. Combine unittest e pytest quando necessário para compatibilidade.

  5. Escreva testes assíncronos para sistemas async.

  6. Integre com CI/CD para execução automática em cada commit.

  7. Sempre limpe recursos externos em fixtures ou teardown.


7. Aplicações Profissionais


8. Conclusão

  • Testes avançados garantem qualidade, confiabilidade e manutenção.

  • Técnicas incluem:

    • unittest com setup/teardown e subtests

    • pytest com fixtures, parametrização e async

    • Mocks complexos com side_effects, context managers e classes inteiras

  • Seguindo boas práticas, é possível criar pipelines de teste robustos e escaláveis, essenciais em produção.

Comentários

Postagens mais visitadas deste blog

Laços de Repetição em Python: Conceitos e Exemplos Práticos

Manipulação de Arquivos no C#: Como Ler, Escrever e Trabalhar com Arquivos de Forma Simples

Como Instalar o Xamarin com C#: Passo a Passo Completo