Módulo 13 - Testes avançados

parametrize e testar exceções

10 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026

O que você vai aprender

  • Usar @pytest.mark.parametrize para rodar um teste com várias entradas.
  • Cobrir casos de borda sem copiar funções de teste.
  • Verificar exceções com pytest.raises.
  • Checar a mensagem da exceção junto com o tipo.

Um teste, muitos casos

Imagine testar uma função que verifica se um número é par. Você quer conferir vários casos: pares, ímpares, zero, negativos. Escrever uma função de teste para cada seria repetitivo, e a repetição é inimiga da manutenção. O parametrize resolve isso com elegância. Você escreve o teste uma vez e fornece uma lista de entradas com os resultados esperados. O pytest roda a função uma vez para cada conjunto, e cada um aparece no relatório como um caso separado, com seus próprios valores.

import pytest

def eh_par(n):
    return n % 2 == 0

@pytest.mark.parametrize("entrada, esperado", [
    (2, True),
    (3, False),
    (0, True),
    (-4, True),
    (-7, False),
])
def test_eh_par(entrada, esperado):
    assert eh_par(entrada) == esperado

Cinco casos, uma função. Cada tupla vira um teste independente no relatório.

Os ganhos são concretos. Se um caso falha, o pytest diz exatamente qual conjunto de dados quebrou, sem confusão. Acrescentar um novo caso é adicionar uma linha à lista, não copiar uma função inteira. E a intenção fica cristalina: a tabela de entradas e saídas esperadas funciona quase como uma documentação do comportamento da função. Esse é o padrão certo sempre que você percebe que está prestes a copiar e colar um teste só mudando os números.

Testar que o erro certo acontece

Testar o caminho de sucesso é metade do trabalho. Um código robusto também levanta erros de propósito quando recebe algo inválido, e isso precisa ser testado. Não faz sentido deixar a exceção explodir e derrubar o teste; você quer justamente afirmar que ela aconteceu. Para isso existe o pytest.raises, usado com with. Você coloca dentro do bloco a chamada que deveria falhar e diz qual exceção espera. Se ela ocorre, o teste passa; se não, o teste falha, porque o erro esperado não veio.

import pytest

def raiz_quadrada(x):
    if x < 0:
        raise ValueError("nao existe raiz real de numero negativo")
    return x ** 0.5

def test_raiz_de_numero_valido():
    assert raiz_quadrada(9) == 3.0

def test_raiz_de_negativo_levanta():
    with pytest.raises(ValueError):
        raiz_quadrada(-1)

def test_raiz_de_negativo_mensagem():
    # match verifica um trecho da mensagem, alem do tipo.
    with pytest.raises(ValueError, match="numero negativo"):
        raiz_quadrada(-4)

with pytest.raises afirma que a exceção acontece; match confere a mensagem.

O parâmetro match, no último teste, acrescenta rigor. Ele verifica que a mensagem da exceção contém o trecho indicado, além de checar o tipo. Isso protege contra um engano sutil: o código levantar a exceção certa pelo motivo errado. Testar a mensagem confirma que a falha é aquela que você esperava. Junto com o parametrize, o pytest.raises fecha o ciclo de cobertura: você testa o que deve funcionar e também o que deve falhar, que é onde muitos bugs se escondem.

Cobrir os casos de borda

Reunindo os dois recursos, você consegue cobrir com poucos linhas o que importa numa função: os valores típicos, os limites e os inválidos. Casos de borda são onde os defeitos moram, o zero, o vazio, o negativo, o valor máximo, e o parametrize deixa listá-los sem esforço. As entradas inválidas, que devem levantar erro, entram com pytest.raises. Uma função bem testada tem essas três frentes cobertas, e o relatório verde passa a significar algo de verdade.

FrenteFerramentaExemplo
Casos típicosparametrizesomar(2, 3) == 5
Casos de bordaparametrizesomar(0, 0), valores negativos, máximos
Entradas inválidaspytest.raisesdividir(1, 0) levanta ZeroDivisionError

Três frentes de teste que juntas dão confiança real numa função.

Um lembrete de bom senso: cobrir não é testar toda combinação imaginável, é escolher os casos que representam classes de comportamento. Para uma faixa de 0 a 100, você não testa os 101 valores; testa um valor no meio, os extremos 0 e 100, e o que fica logo fora da faixa para conferir o erro. Poucos casos bem escolhidos cobrem muito. Essa mentalidade, aliada ao parametrize e ao pytest.raises, produz suítes enxutas que ainda assim pegam o que precisa ser pego.

Teste rápido

Como você testa que uma função levanta ValueError para uma entrada inválida?

Perguntas frequentes

Cada conjunto do parametrize é um teste separado?
Sim. O pytest executa a função de teste uma vez para cada conjunto de dados e trata cada execução como um caso independente. No relatório, eles aparecem separados, então você sabe exatamente qual entrada falhou, sem ambiguidade. Adicionar um caso é só incluir mais uma linha na lista.
Por que não usar um for dentro do teste em vez de parametrize?
Um for pararia no primeiro assert que falhasse e não diria qual iteração quebrou com clareza. Com parametrize, cada caso roda de forma isolada, todos são reportados e a falha aponta a entrada exata. O relatório fica muito mais informativo do que um laço manual.
pytest.raises captura a exceção como um except comum?
Ele funciona como um bloco que espera a exceção. Se a exceção indicada ocorre dentro do with, o teste segue e passa; se não ocorre, o teste falha. Você pode inspecionar a exceção capturada pelo atributo value do objeto retornado, útil para checar detalhes além do tipo.
Para que serve o parâmetro match do pytest.raises?
Para verificar que a mensagem da exceção contém um trecho esperado, além do tipo. Isso evita passar um teste por acaso quando o código levanta a exceção certa pelo motivo errado. É uma checagem a mais de que a falha é exatamente aquela que você quis provocar.
Quantos casos de borda devo testar?
O suficiente para representar as classes de comportamento, não toda combinação possível. Para uma faixa, teste um valor típico, os limites e um valor logo fora dela. Poucos casos bem escolhidos cobrem a maior parte dos defeitos, mantendo a suíte enxuta e rápida.

Fontes

Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.