Módulo 15 - Testes automatizados
Prática: testar uma função
11 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026
O que você vai aprender
- Escrever uma função de cálculo com uma regra clara e validação de entrada.
- Cobrir o caso normal e os casos de borda com assert.
- Reescrever a mesma cobertura como uma classe unittest.
- Fechar o módulo com um método de testar que dá para repetir sempre.
Ouvir o resumo desta aula
Um recap de cerca de 2 minutos na voz do Valim, para ouvir no trânsito ou na academia.
Ler a transcrição do resumo
Resumo da aula: Prática: testar uma função.
Os objetivos desta aula. Escrever uma função de cálculo com uma regra clara e validação de entrada. Cobrir o caso normal e os casos de borda com assert. Reescrever a mesma cobertura como uma classe unittest. Fechar o módulo com um método de testar que dá para repetir sempre.
Veja o essencial, parte por parte.
A função que vamos testar. Vamos calcular o preço de uma corrida: bandeirada + valor por quilômetro.
Cobrindo com assert e depois com unittest. 1. Escreva a função de cálculo, com validação da entrada.
Fechando o módulo. Você acabou de percorrer o ciclo inteiro de testar uma função, e é esse ciclo que vai se repetir na sua vida de programador.
Esse foi o resumo do essencial. Para se aprofundar, leia a aula completa e responda os exercícios.
A função que vamos testar
Nada fixa melhor o módulo do que testar uma função de verdade. Vamos usar um cálculo simples e concreto: o preço de uma corrida de táxi. A regra é uma bandeirada fixa, um valor por quilômetro rodado e a distância percorrida. O preço é a bandeirada mais o valor por quilômetro vezes a distância. É uma função de cálculo pura: dadas as mesmas entradas, sempre devolve o mesmo resultado, o que a torna ideal para testar. Antes de calcular, ela valida a entrada, porque uma distância negativa não faz sentido.
def preco_corrida(distancia_km, bandeirada=5.0, por_km=2.5):
if distancia_km < 0:
raise ValueError("distancia nao pode ser negativa")
return bandeirada + por_km * distancia_km
print(preco_corrida(10)) # 30.0 (5 + 2.5 * 10)
print(preco_corrida(0)) # 5.0 (so a bandeirada)
print(preco_corrida(4, 6)) # 16.0 (6 + 2.5 * 4)A função de cálculo, com valores padrão de bandeirada e preço por quilômetro.
Antes de testar, pense nos casos como você aprendeu na aula anterior. O caso normal é uma distância comum, como 10 km. A borda mais óbvia é a distância zero, onde o preço deve ser só a bandeirada. Vale testar também uma distância que não seja inteira, e a possibilidade de mudar a bandeirada e o valor por quilômetro. E, claro, a entrada inválida: uma distância negativa, que deve levantar ValueError. Com essa lista na cabeça, escrever os testes vira quase mecânico.
Cobrindo com assert e depois com unittest
Comece pelo jeito mais direto, com asserts soltos em um arquivo. Cada linha afirma um resultado esperado, e a mensagem ajuda se algo falhar. Rode e, se o programa chegar ao print final em silêncio, todos os casos passaram.
assert preco_corrida(10) == 30.0, "caso normal: 10 km"
assert preco_corrida(0) == 5.0, "borda: distancia zero"
assert preco_corrida(2) == 10.0, "2 km: 5 + 2.5 * 2"
assert preco_corrida(4, bandeirada=6, por_km=3) == 18.0, "tarifas custom"
erro = False
try:
preco_corrida(-1)
except ValueError:
erro = True
assert erro, "distancia negativa deveria levantar ValueError"
print("Todos os casos passaram!")
# Saida: Todos os casos passaram!Cobertura com assert: caso normal, bordas, tarifas próprias e a entrada inválida.
Funciona, mas testar a exceção com try, except e uma variável de controle é desajeitado. É exatamente aí que o unittest brilha, com o assertRaises que você viu. Reescreva a mesma cobertura como uma classe de testes: cada caso vira um método test_ com nome descritivo, e a verificação de erro fica limpa. Este é o formato que você levaria para um projeto de verdade.
import unittest
class TestPrecoCorrida(unittest.TestCase):
def test_caso_normal(self):
self.assertEqual(preco_corrida(10), 30.0)
def test_distancia_zero(self):
self.assertEqual(preco_corrida(0), 5.0)
def test_tarifas_customizadas(self):
self.assertEqual(preco_corrida(4, bandeirada=6, por_km=3), 18.0)
def test_distancia_negativa(self):
with self.assertRaises(ValueError):
preco_corrida(-1)
if __name__ == "__main__":
unittest.main()
# Ran 4 tests in 0.000s
# OKA mesma cobertura em unittest: quatro comportamentos, quatro métodos, relatório limpo.
Fechando o módulo
Você acabou de percorrer o ciclo inteiro de testar uma função, e é esse ciclo que vai se repetir na sua vida de programador. Primeiro veio a função, com uma regra clara e uma validação que recusa entrada sem sentido. Depois a lista de casos, pensada de propósito para incluir a borda do zero e a entrada negativa, não só o caminho feliz. Então a cobertura, primeiro no assert cru para ver a mecânica e depois no unittest, mais organizado e com o assertRaises resolvendo a verificação de exceção com elegância. O resultado é uma suíte que roda em um instante e que, a partir de agora, protege essa função: se alguém mexer na fórmula ou remover a validação, um teste falha e avisa. Guarde o método dos três passos, porque ele vale para qualquer função de cálculo que você escrever. No próximo módulo, o projeto final junta tudo o que o curso ensinou, e os testes vão ser parte dele.
Teste rápido
Na prática, por que a distância zero e a distância negativa foram testadas além do caso de 10 km?
Perguntas frequentes
- Por que essa função de tarifa é boa para praticar testes?
- Porque é uma função de cálculo pura: com as mesmas entradas ela sempre devolve o mesmo resultado e não depende de nada externo. Isso torna o teste direto, bastando comparar o valor devolvido com o esperado. Cálculos e regras de negócio são justamente o que mais vale automatizar.
- Preciso escrever os testes com assert antes de usar unittest?
- Não é obrigatório, foi uma escolha didática para mostrar a mesma cobertura nos dois estilos. Em um projeto real você iria direto ao unittest ou ao pytest. O assert cru serve para entender a mecânica; o framework organiza os testes e trata as exceções com mais elegância.
- Como decido quais casos testar em uma função nova?
- Siga o método dos três passos: escreva a função com validação, liste os casos (o normal, as bordas como zero e limites, e as entradas inválidas) e transforme cada caso em um teste com nome descritivo. Pergunte-se onde a função poderia tropeçar e escreva um teste para lá.
- Por que o unittest ficou mais limpo que os asserts para testar o erro?
- Porque testar exceção com assert exige um try, except e uma variável de controle, o que é desajeitado. O unittest oferece with self.assertRaises(ValueError):, que verifica a exceção esperada em uma linha clara. É um bom exemplo de por que os frameworks facilitam a vida.
- O que ganho ao deixar esses testes prontos?
- Uma rede de proteção. Se alguém mudar a fórmula do preço ou remover a checagem de distância negativa, um teste falha na hora e aponta o problema. Você pode refatorar a função, mudar valores padrão ou reorganizar o código com a segurança de que os comportamentos cobertos continuam valendo.
- Esse método serve para qualquer função?
- Serve muito bem para funções de cálculo e regras claras, que recebem valores e devolvem um resultado. Funções que dependem de tela, arquivo ou rede pedem técnicas a mais, mas o coração é o mesmo: escolher os casos, comparar obtido e esperado e testar também as bordas e as entradas inválidas.
Fontes
Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.