Depuração e
mini-kata TDD
O teste avisou que existe um bug. Agora é preciso encontrá-lo — e depois inverter o jogo, escrevendo o teste antes do código. Fecho da semana da qualidade.
O teste avisou que existe um bug. Agora é preciso encontrá-lo — e depois inverter o jogo, escrevendo o teste antes do código. Fecho da semana da qualidade.
Diferenciar erro de sintaxe, de execução e de lógica — e saber qual deles o computador não avisa
Usar breakpoints, passo a passo e inspeção de variáveis para achar a causa raiz
Executar o ciclo red-green-refactor em uma função pequena, do zero
Integrar documentação, teste e depuração em um ciclo completo, trabalhando em dupla
A média de um programador experiente é gastar mais tempo procurando a causa de um erro do que digitando a correção. A correção costuma ter uma linha; a investigação, três horas.
Virem e conversem: qual foi o bug mais difícil que vocês já enfrentaram? O que vocês tentaram — e o que finalmente revelou a causa?
O código nem roda. O interpretador aponta o arquivo e a linha.
Fácil: falta de dois-pontos, parêntese aberto, indentação errada.Roda e para no meio, com mensagem de erro.
Médio:ZeroDivisionError, IndexError, TypeError.
Roda até o fim, sem reclamar — e entrega o resultado errado.
Difícil: ninguém avisa. É aqui que a depuração vale ouro.Traceback (most recent call last):
File "app.py", line 22, in <module>
print(media_turma(notas))
File "app.py", line 15, in media_turma
return soma / len(lista)
ZeroDivisionError: division by zero
Comece pela última linha: o tipo do erro e a mensagem
Suba: a linha imediatamente acima mostra onde estourou
Continue subindo para ver quem chamou quem
A causa raiz costuma estar na origem dos dados, não no ponto do estouro
Colocar print nos pontos-chave é a técnica mais antiga e ainda a mais usada do mundo. Funciona — desde que os prints sejam rotulados.
print(x)
print(y)
print(total)
3 0 0
print("[loop] i =", i, "| total =", total)
[loop] i = 0 | total = 8 [loop] i = 1 | total = 6 [loop] i = 2 | total = 7
print mostra o que você lembrou de imprimir, no ponto em que você achou que era o problema. E dá para esquecer um deles no código entregue.
O depurador (debugger) permite congelar o programa em qualquer ponto e olhar a memória por dentro, sem alterar uma linha do código.
| Comando | O que faz | Quando usar |
|---|---|---|
| Breakpoint | Marca a linha onde a execução deve parar | Antes do trecho suspeito |
| Step over | Executa a linha atual e para na próxima | Avançar dentro do laço |
| Step into | Entra na função chamada nessa linha | Suspeita da função chamada |
| Step out | Termina a função atual e volta a quem chamou | Já viu o que precisava |
| Continue | Roda até o próximo breakpoint | Pular iterações sem interesse |
F9 marca o breakpoint, F5 inicia a depuração e F10 avança linha a linha.
Com o programa congelado, os painéis Locals (todas as variáveis do escopo atual) e Watch (as expressões que você escolheu vigiar) mostram os valores naquele exato instante.
notas = [8, 6, 7, 9]
total = 0
for nota in notas:
total = nota # ← sobrescreve!
print("Média:", total / len(notas))
Média: 2.25 (o certo seria 7.5)
nota=8 → total=8 (ok por acaso)
nota=6 → total=6 ← aqui!
nota=7 → total=7
nota=9 → total=9
= por +=. Mas note: o programa rodou até o fim sem reclamar — só a inspeção do estado revelou o defeito.
Durante a depuração de uma estrutura de repetição, qual é a finalidade principal da ferramenta de inspeção de variáveis?
Reproduzir — encontre a entrada exata que provoca o erro. Bug que não se reproduz não se corrige.
Isolar — reduza o cenário ao menor trecho que ainda falha (bissecção: corte pela metade, teste, repita).
Levantar hipótese — "acho que o total está sendo sobrescrito no laço". Uma hipótese por vez.
Testar a hipótese — breakpoint + inspeção. Confirmou? Siga. Não confirmou? Descarte e volte ao passo 3.
Corrigir e blindar — arrume a causa e escreva um teste unitário que falharia antes da correção.
A técnica do rubber duck debugging consiste em explicar o código linha por linha, em voz alta, para um objeto — clássico: um patinho de borracha na mesa.
Funciona porque explicar obriga o cérebro a verbalizar as suposições que ele estava pulando na leitura silenciosa. Na maior parte das vezes a pessoa se interrompe no meio da frase: "ah, espera…".
iflista[10] numa lista de 3 itensDepurar é caro. A pergunta natural é: dá para escrever código de um jeito que gere menos bugs para caçar? Quando o teste vem depois, ele quase sempre é escrito para passar — a gente olha o código pronto e confirma o que ele já faz, inclusive os bugs. Quando vem antes, ele descreve o que o código deveria fazer.
Pense: se você precisasse explicar para outra pessoa o que a função deve fazer, sem mostrar o código, o que escreveria? Essa frase é o seu primeiro teste.
return. É exatamente o que vimos na Semana 17.
RED
Escreva um teste para um comportamento que ainda não existe. Rode: ele deve falhar.
GREEN
Escreva a implementação mínima que faz o teste passar. Feio pode; errado não.
REFACTOR
Melhore o código com a rede de proteção ligada. Os testes seguem verdes.
def testar_soma():
# Arrange
esperado = 7
# Act
obtido = calcular(3, 4, "+")
# Assert
assert obtido == esperado, \
"3 + 4 deveria dar 7"
testar_soma()
Traceback (most recent call last): File "kata.py", line 10, in <module> NameError: name 'calcular' is not defined
calcular nem existe. O teste acabou de definir a assinatura que ela precisa ter: dois números e um operador.
def calcular(n1, n2, op):
if op == "+":
return n1 + n2
testar_soma()
(sem saída — o assert passou em silêncio)
def testar_divisao_por_zero():
obtido = calcular(10, 0, "/")
assert obtido is None, \
"Divisão por zero deve devolver None"
testar_divisao_por_zero()
AssertionError: Divisão por zero deve devolver None
def calcular(n1, n2, op):
if op == "+":
return n1 + n2
if op == "/":
if n2 == 0:
return None
return n1 / n2
(silêncio: os dois testes passam)
def calcular(n1, n2, op):
"""
Executa uma operação aritmética simples.
Args:
n1 (float): primeiro operando.
n2 (float): segundo operando.
op (str): "+", "-", "*" ou "/".
Returns:
float: resultado da operação.
None: se o operador for inválido ou
se houver divisão por zero.
"""
if op == "+":
return n1 + n2
if op == "-":
return n1 - n2
if op == "*":
return n1 * n2
if op == "/":
return None if n2 == 0 else n1 / n2
return None
OK: testar_soma OK: testar_subtracao OK: testar_multiplicacao OK: testar_divisao OK: testar_divisao_por_zero OK: testar_operador_invalido
Antes de qualquer implementação. O que isso significa?
Código nasce testável: funções curtas, com entrada e saída explícitas.
Refatorar deixa de ser risco e vira rotina.Disciplina. O ciclo é curto e repetitivo, e no começo parece mais lento.
O tempo economizado aparece depois, na manutenção — e nas horas de depuração que não aconteceram.Navegador escreve o teste (red) e explica o comportamento esperado.
Piloto escreve a implementação mínima (green).
Refatoram juntos e trocam os papéis a cada ciclo.
Com o código documentado, testado e depurável, a Semana 19 volta aos dados: manipulação de strings — e cada função nova já nasce com teste.
SEDUC-SP. Educação Profissional Paulista — Técnico em Desenvolvimento de Sistemas. Lógica de Programação, Unidade 5, Componente 1 — Documentação, testes unitários e depuração (Semana 18, aulas 3 e 4). Códigos: SISANO1C1B3S18A3 e A4.
BECK, K. Desenvolvimento guiado por testes. Porto Alegre: Bookman, 2010.
ASCENCIO, A. F. G.; CAMPOS, E. A. V. Fundamentos da programação de computadores. São Paulo: Pearson Education do Brasil, 2012.
FORBELLONE, A. L. V.; EBERSPÄCHER, H. F. Lógica de programação: a construção de algoritmos e estruturas de dados. São Paulo: Pearson, 2005.
CÓDIGO FONTE TV. TDD (Test Driven Development) — Dicionário do Programador. YouTube, 10 set. 2018. Disponível em: youtube.com. Acesso em: 23 abr. 2026.
Aula compilada (dobradinha) · adaptação para apresentação HTML por Guilherme Antunes