Uma entrada de execução sem efeitos na importação
Um módulo de suporte deve poder ser importado sem enviar relatórios ou iniciar jobs. Mantém funções reutilizáveis separadas de main e chama main sob a guarda __name__ == "__main__". Define também o contrato do processo: mensagens nos canais previstos e um código inteiro que o scheduler consiga interpretar. Uma string devolvida por main e passada a sys.exit não significa sucesso; gera código 1 e texto em stderr. Com argparse, uma entrada inválida é normalmente rejeitada antes da execução, com código 2. Num ensaio, confirma separadamente importação, argumentos válidos, argumentos inválidos e conclusão do trabalho.
Testar uma obrigação que pode falhar
Antes de escrever o teste, identifica a obrigação observável. Se uma quantidade deve ser um inteiro entre zero e cem, cobre zero, cem e valores imediatamente fora dos limites, além de um caso normal. Para exigir uma exceção, usa assertRaises; um try que simplesmente ignora ValueError também pode passar quando nada é rejeitado. Testa o resultado e os efeitos permitidos, incluindo a ausência de envio quando a validação falha. Um teste verde só demonstra as condições que foram realmente exercitadas. Regista as fronteiras que ainda dependem de integração, concorrência real ou dados com dimensão representativa.
Substituir a dependência efetivamente utilizada
Um patch precisa de atingir o nome que o código procura. Se pipeline importou deliver diretamente, substituir apenas sender.deliver depois dessa importação pode deixar pipeline com a referência anterior. Usa um sentinela local para demonstrar qual função foi chamada, sem enviar dados para a rede. Autospec ajuda a detetar argumentos incompatíveis, mas não comprova o schema de uma resposta. Configura retornos, exceções e sequências coerentes com o contrato. Num teste de retoma, verifica a identidade de cada chamada, o limite de tentativas e o resultado comunicado. A deduplicação no serviço externo exige documentação e evidência próprias.
Fixtures independentes e limpeza previsível
Cada teste deve controlar os recursos e o estado que altera. Regista addCleanup assim que adquirires um recurso que precisa de libertação; esses cleanups podem correr mesmo quando setUp falha e tearDown não é chamado. Usa diretorias e objetos próprios da fixture, sem apagar recursos de outros processos. Se mudares configuração global, restaura-a ou redesenha a dependência para receber configuração explícita. Correr um teste sozinho e em ordem diferente ajuda a descobrir contaminação. Fixar uma ordem favorável ou repetir até ficar verde não demonstra independência e pode ocultar exatamente o comportamento que a suite devia detetar.
Reproduzir o ambiente e interpretar a observabilidade
Um ambiente virtual não deve ser tratado como pasta portável entre máquinas. Recria-o com o intérprete e as dependências previstos e ensaia a chamada usada pelo scheduler. Confirma caminhos, diretoria e configuração, sem depender da ativação manual de um terminal. Nos logs, distingue uma operação de cada emissão do respetivo evento. Um handler no logger filho e outro no root, com propagação ativa para o mesmo destino, podem produzir linhas repetidas. Antes de compensar uma entrega, verifica a referência e o estado no destinatário. Depois corrige a configuração de logging e testa a quantidade de emissões esperada.
from unittest.mock import create_autospec
def send(batch_id, *, timeout):
raise RuntimeError("external client not configured")
def deliver(batch_id, client):
response = client(batch_id, timeout=2)
if not isinstance(response, dict) or response.get("accepted") is not True:
raise ValueError("invalid acceptance response")
return batch_id
client = create_autospec(send, return_value={"accepted": True})
assert deliver("TRAIN-204", client) == "TRAIN-204"
client.assert_called_once_with("TRAIN-204", timeout=2)
client.return_value = {"status": "accepted"}
try:
deliver("TRAIN-205", client)
except ValueError:
print("response contract rejected")
else:
raise AssertionError("invalid response accepted")Executa o exemplo local: não há cliente de rede configurado. O mock respeita a assinatura, mas a segunda resposta tem outro schema e deve ser rejeitada. Acrescenta um teste com assertRaises e confirma que um timeout posicional é recusado pelo autospec. Este ensaio comprova decisões locais, não o funcionamento de um serviço externo.
Armadilhas comuns
Testes sem asserção; patch no namespace errado; autospec tratado como validação de payload; estado global deixado pela fixture; tearDown assumido após setUp falhado; sucesso comunicado por string; duplicação inferida apenas dos logs.
Tópicos relacionados: Funções pequenas, contratos claros · Erros e ficheiros com contexto · Automação observável e repetível · Subprocessos, caminhos e resultados verificáveis
Um teste útil falha quando a obrigação é violada, controla os seus próprios efeitos e explicita o limite entre evidência local e comportamento externo.
Referência: Python 3.14: Unit testing framework · Python 3.14; DR Python 2026.3