Configurando o Pytest e Entendendo Teste Unitário
Um artigo conceitual sobre configuração, execução e conceitos fundamentais de testes com pytest.
Pytest é um framework de teste universal que simplifica a escrita de testes no Python, permitindo criar baterias de testes organizadas, parametrizadas e detalhadas.
O Que é Pytest?
Pytest é uma ferramenta de testagem amplamente utilizada no ecossistema Python. Diferente do unittest padrão do Python, pytest oferece um paradigma mais simples e flexível para escrever testes unitários e de integração. A biblioteca utiliza o pytest-discover, que automatiza a descoberta de arquivos de teste baseados em convenções de nomenclatura (nomes começam com test_ e terminam com _test.py).
O framework foi desenvolvido para ser minimalista, mas potente — permitindo testar desde funções simples até integrações complexas, com suporte nativo para parametrização, mocking, fixtures e geração de relatórios detalhados.
Por Que Usar Pytest?
- Sintaxe Simplificada: Testes mais curtos e legíveis comparados ao unittest.
- Descoberta Automática: O pytest detecta automaticamente os testes conforme você executa o comando.
- Flexibilidade em Configuração: Suporta arquivos de configuração (
pyproject.toml,setup.cfg,pytest.ini) para adaptar comportamentos como verbosidade e paths de busca. - Ferramentas Avançadas: Recursos como fixtures, parametrização, assertions e exceções customizadas.
- Comunidade Ativa: Maior adoção na indústria, com extensões e plugins que estendem suas funcionalidades.
Configuração do Pytest no Projeto
Para executar testes automaticamente, é necessário configurar corretamente o ambiente. No caso de projetos modernos, a configuração é feita via pyproject.toml usando ferramentas como Poetry ou pip.
O pytest executa seus testes buscando por arquivos que começam com test_ e terminam em _test.py, além de métodos dentro desses arquivos que começam com test_.
# pyproject.toml
[tool.poetry.dependencies]
pytest = "^8.2.2"Depois de instalar as dependências, execute os testes via terminal:
pytestSe não houver arquivos na pasta tests, o pytest retornará uma mensagem como “0 tests in 0 seconds”, indicando que não foram encontrados.
A Convenção de Nomenclatura de Testes
O nome do arquivo deve seguir convenções estritas para ser descoberto pelo pytest:
- Arquivos devem começar com
test_ - Devem ter
_test.pycomo sufixo (ou terminar em.pyse não forem arquivos específicos, mas a prática mais comum é incluir o sufixo)
Exemplos válidos:
test_myservice.pyconftest.py(arquivo especial para fixtures globais)test_utils_pytest.py(caso você incluapytestno nome do arquivo, pois o pytest usa a palavra-chave como parte da busca)
Os métodos dentro desses arquivos devem seguir a mesma regra: começar com test_. Exemplo:
def test_meu_metodo():
passComo Escrever Seu Primeiro Teste
O primeiro passo é criar uma função que será testada. Por exemplo, criaremos um método que eleve um número ao quadrado na pasta utils:
# utils.py
def eleva_quadrado(x):
return x ** 2Agora, crie o arquivo de teste. O nome do arquivo deve seguir a convenção: começar com test_ e terminar com _test.py. No exemplo, criamos test_utils_pytest.py:
# test_utils_pytest.py
from utils import eleva_quadrado
def test_eleva_quadrado_sucesso():
resultado = eleva_quadrado(2)
assert resultado == 4Observe a sintaxe crucial: o pytest só executa uma validação se você usar a palavra-chave assert. Sem ela, o Python não verifica automaticamente o valor esperado.
O Importante da Palavras-Chave Assert
A palavra-chave assert é uma funcionalidade do Python que permite criar comparações condicionais dentro de testes. Se a condição for falsa, o teste falha e uma mensagem de erro detalhada é exibida — indicando qual linha falhou e o porquê.
Exemplo de uso:
# assert 4 == 10 → AssertionError (teste falha)
assert 4 == 10
# assert 4 == 4 → Assertion passes (teste passa)
assert 4 == 4Se você omitir a palavra assert, o Python não realiza essa verificação. Isso é fundamental para entender por que testes sem assert são válidos apenas de forma superficial — eles passam sempre, mesmo com dados incorretos.
Executando e Analisando os Testes
Pytest oferece várias opções de configuração para modificar o comportamento da execução:
-q: Reduz a verbosidade (menos detalhes).-v: Aumenta a verbosidade (mostra mais detalhes).--verboseou-vv: Mostra informações extras sobre falhas, erros e testes.--minfail X: Fica executando até que X testes falhem.--q(quiet): Desliga os relatórios padrão do pytest.
Ao executar com pytest -vv, você verá detalhadamente cada teste:
pytest -vvEsse comando exibe o nome do módulo e cada teste, bem como sua status de passed ou failed.
Teste Parametrizado: O Poder das Tuplas
Um dos recursos mais potentes do pytest é a parametrização. Isso permite que um único método de teste seja executado múltiplas vezes com diferentes entradas. A forma padrão envolve tuplas, onde cada elemento representa um par (entrada_esperada, resultado_esperado).
Exemplo:
def test_eleva_quadrado_parametrizado(test_input, expected):
resultado = eleva_quadrado(test_input)
assert resultado == expectedA chamada de teste seria assim:
@pytest.mark.parametrize("test_input, expected", [
(2, 4),
(10, 100),
(3, 9),
])
def test_eleva_quadrado_parametrizado(test_input, expected):
resultado = eleva_quadrado(test_input)
assert resultado == expectedO pytest executará o teste três vezes: uma para cada tupla fornecida. Isso torna seus testes mais concisos e evita repetição de código.
Teste de Exceção com raises
Para testar se um método lança uma exceção específica, use a função pytest.raises. Essa funcionalidade permite capturar a exceção lançada pelo teste e verificar o conteúdo (como mensagem ou dados).
Exemplo:
def test_eleva_quadrado_excecao_tipo_error():
try:
resultado = eleva_quadrado("A") # "A" é string, não número
assert False # Nunca deve chegar aqui
except TypeError as e:
assert isinstance(e.value, TypeError)Pytest oferece também uma maneira mais direta com pytest.raises:
def test_eleva_quadrado_excecao_typo_error():
with pytest.raises(TypeError) as e:
eleva_quadrado("A")
assert "str" in str(e.value), e.excon.textA função captura a exceção, permitindo que você compare o conteúdo ou verifique propriedades específicas.
Parametrizando Testes de Exceções
Para testar diversos cenários de erros, use parametrização junto com pytest.raises. Exemplo:
@pytest.mark.parametrize("test_input", [
1,
None,
])
def test_eleva_quadrado_parametra_excecao(test_input):
try:
eleva_quadrado(test_input)
assert False # Nunca deve chegar aqui
except TypeError as e:
assert "str" in str(e.value), e.excon.textEsse teste será executado duas vezes: uma com entrada 1 (que é número, mas o método pode não suportá-lo de forma adequada) e outra com entrada None (também inválido).
Dicas e Boas Práticas
- Seja Específico: Testes claros e específicos facilitam a manutenção.
- Use Asserts Corretamente: Sempre que possível, valide o resultado esperado em testes unitários.
- Monitore Falhas: Use relatórios como
--verbosepara identificar falhas mais rapidamente. - Organize Pastas de Teste: Mantenha arquivos de teste bem organizados (ex: por módulo ou funcionalidade).
- Documentar Casos de Uso: Use parametrização para documentar cenários variados em um único teste.
Próximos Passos
- Aprenda sobre fixtures (funções de configuração) para gerenciar dependências entre testes.
- Estude como usar mocks para isolar dependências externas e simular comportamentos.
- Explore plugins do Pytest, como
pytest-cov, que gera relatórios detalhados de cobertura de código.
Para continuar aprendendo:
Este artigo conceitual cobre os fundamentos para configurar, escrever e executar testes unitários com pytest. Aparamentos chave: convenções de nomenclatura, uso de assert, parametrização, e tratamento de exceções.