Testes automatizados em Python com Pytest, do básico ao avançado
Testes automatizados com Pytest: fixtures, mocks, cobertura e integração com Django
Testar manualmente funciona até o momento em que você não lembra mais o que precisava testar. Pytest automatiza isso: você descreve o que espera, e ele avisa quando o código deixa de se comportar como prometido — antes do usuário descobrir.
Instalação e primeiro teste
pip install pytest
Crie calculadora.py:
def somar(a, b):
return a + b
def dividir(a, b):
if b == 0:
raise ValueError("Divisão por zero.")
return a / b
Crie test_calculadora.py:
from calculadora import somar, dividir
import pytest
def test_soma_positivos():
assert somar(2, 3) == 5
def test_soma_negativos():
assert somar(-1, -1) == -2
def test_dividir_normal():
assert dividir(10, 2) == 5.0
def test_dividir_por_zero():
with pytest.raises(ValueError, match="Divisão por zero"):
dividir(10, 0)
pytest # roda todos os testes
pytest -v # verbose (mostra nome de cada teste)
pytest test_calc.py # só um arquivo
Fixtures: preparar dados reutilizáveis
Em vez de repetir a criação de objetos em cada teste, use fixtures:
import pytest
from models import Carrinho, Produto
@pytest.fixture
def carrinho_com_itens():
c = Carrinho()
c.adicionar(Produto("Caneta", 2.50), quantidade=3)
c.adicionar(Produto("Caderno", 15.00), quantidade=1)
return c
def test_total(carrinho_com_itens):
assert carrinho_com_itens.total() == 22.50
def test_quantidade_itens(carrinho_com_itens):
assert carrinho_com_itens.total_itens() == 4
Mocks: isolar dependências externas
Quando seu código chama uma API externa, banco ou serviço de email, você não quer que os testes dependam disso. Mocks substituem a chamada real por um valor falso controlado.
from unittest.mock import patch, MagicMock
from servicos import buscar_cotacao
def test_cotacao_dolar(mocker):
mock_response = MagicMock()
mock_response.json.return_value = {"USD": {"bid": "5.10"}}
with patch("servicos.requests.get", return_value=mock_response):
cotacao = buscar_cotacao("USD")
assert cotacao == 5.10
Cobertura de testes
pip install pytest-cov
pytest --cov=. --cov-report=term-missing
O relatório mostra quais linhas do seu código nunca foram tocadas por nenhum teste — cada linha sem cobertura é um bug esperando para acontecer em produção.
Testando Django com pytest-django
pip install pytest-django
Crie pytest.ini na raiz do projeto:
[pytest]
DJANGO_SETTINGS_MODULE = config.settings
import pytest
from django.test import Client
from tutoriais.models import Tutorial
@pytest.mark.django_db
def test_tutorial_publicado_aparece_na_lista():
Tutorial.objects.create(
titulo="Teste", slug="teste", resumo="...", publicado=True
)
client = Client()
response = client.get("/tutoriais/")
assert response.status_code == 200
assert "Teste" in response.content.decode()
O decorator @pytest.mark.django_db permite que o teste acesse o banco. O pytest-django cria um banco temporário para cada execução de testes — seus dados reais ficam intactos.
Próximos passos
Com testes no lugar, você pode refatorar com segurança. O próximo tutorial aplica isso construindo uma API REST completa com FastAPI.