Zombando em Python: unittest.mock, patch e os erros que fazem os testes mentirem

  1. Início
  2. Blog
  3. Automação de testes

Automação de Teste

Zombando em Python: unittest.mock, patch e os erros que fazem os testes mentirem

As simulações permitem testar o código sem uma rede, banco de dados ou relógio real. Usados ​​descuidadamente, eles também deixam passar códigos quebrados. Aqui está como usar unittest.mock bem, com cada exemplo executado.

8 min de leitura
Zombando em Python: unittest.mock, patch e os erros que fazem os testes mentirem

Principais pontos

  • Um simulado representa um objeto real, como um cliente de API, para que os testes sejam executados de forma rápida e off-line.
  • Use return_value para uma resposta fixa e side_effect para erros ou uma sequência.
  • Corrija o nome onde é usado, não onde está definido. Este é o bug mais comum.
  • Use spec ou autospec para que erros de digitação e argumentos errados falhem em vez de passarem silenciosamente.

Sua função chama uma API meteorológica. Seu teste não deve depender da internet, de uma chave de API ou do clima de amanhã. O integrado unittest.mock do Python permite substituir essa chamada por um substituto que retorna exatamente o que você escolher.

Escrevemos um pequeno módulo weather e nove testes, todos executados com o unittest do Python 3.13. Todos os nove foram aprovados e os resultados impressos abaixo vêm dessa execução.

Por que zombar?

  • Velocidade: sem chamadas de rede ou configuração de banco de dados.
  • Confiabilidade: os testes não falham porque um serviço de terceiros está inativo.
  • Controle: você pode simular tempos limite, erros e dados estranhos sob demanda.
  • Isolamento: você testa sua lógica, não o servidor de outra pessoa.

As simulações pertencem principalmente a testes unitários. Os testes de integração ainda devem falar com dependências reais ou realistas.

Noções básicas de simulação

from unittest.mock import Mock

api = Mock()
api.get_price.return_value = 499

print(api.get_price("pen"))                # 499
api.get_price.assert_called_once_with("pen")
print(api.get_price.call_args_list)        # [call('pen')]
print(api.anything_at_all())               # <Mock name='mock.anything_at_all()' ...>

A Mock registra cada chamada, para que você possa verificar como ela foi usada. Observe a última linha, no entanto. Um Mock simples inventa qualquer atributo que você solicitar. Essa flexibilidade é conveniente e também perigosa, como veremos.

MagicMock é a mesma ideia, mas também suporta métodos “mágicos” como __len__ e __iter__. patch() usa MagicMock por padrão.

side_effect: erros e sequências

side_effect controla o que acontece em cada chamada. Defina-o como uma exceção para simular uma falha:

api.fetch.side_effect = ConnectionError("down")
api.fetch()   # raises ConnectionError

Configure-o em uma lista para retornar resultados diferentes em chamadas sucessivas. Isso é perfeito para testar a lógica de repetição:

def fetch_with_retry(api, attempts=3):
    for _ in range(attempts):
        try:
            return api.fetch()
        except ConnectionError:
            continue
    raise ConnectionError("all attempts failed")

api = Mock()
api.fetch.side_effect = [ConnectionError("busy"), {"ok": True}]
print(fetch_with_retry(api))    # {'ok': True}
print(api.fetch.call_count)     # 2

A primeira chamada falhou, a segunda foi bem-sucedida e o teste provou que a função foi repetida exatamente uma vez. Simular isso em um serviço real seria quase impossível.

patch(): substitui algo temporariamente

Freqüentemente, a dependência não é transmitida. Ela é importada dentro do módulo. patch() troca-o durante um teste e restaura-o depois:

# weather.py
from http_client import get as requests_get

def report(city):
    data = requests_get(f"https://api.example.com/weather?city={city}")
    return f"{city}: {data['temp_c']}°C"
from unittest.mock import patch

with patch("weather.requests_get") as fake_get:
    fake_get.return_value = {"temp_c": 31}
    print(weather.report("Pune"))   # Pune: 31°C
    fake_get.assert_called_once_with(
        "https://api.example.com/weather?city=Pune")

Você pode usar patch como um gerenciador de contexto como este, ou como um decorador, que passa o mock como argumento.

Patch onde é usado, não onde é definido

Este é o erro que quase todo mundo comete uma vez. A função real get reside em http_client, então parece natural corrigi-la lá:

with patch("http_client.get") as fake_get:
    fake_get.return_value = {"temp_c": 99}
    weather.report("Pune")

# ConnectionError: no network in tests: https://api.example.com/weather?city=Pune

A função real foi executada. Quando weather.py fez from http_client import get as requests_get, ele criou seu próprio nome apontando para a função original. O patch http_client.get mudou para um nome diferente, então weather nunca viu o mock. Nosso http_client.get aumentou de propósito, então o erro foi flagrante. Em um projeto real, ele pode chamar silenciosamente uma API ativa.

Regra: corrija o nome do módulo em teste, escrito como "module_under_test.name". Se o módulo fez import http_client e chama http_client.get(), então o patch "http_client.get" está correto, porque a pesquisa acontece no momento da chamada.

spec e autospec: torne as zombarias rigorosas

Um Mock simples aceita qualquer coisa, incluindo erros de digitação. Com uma especificação, ela permite apenas atributos que a classe real possui:

api = Mock(spec=weather.WeatherClient)
api.get_temprature("Pune")
# AttributeError: Mock object has no attribute 'get_temprature'

loose = Mock()
loose.get_temprature("Pune")    # returns a Mock, no error at all

A simulação solta aceitou alegremente o método com erro ortográfico, portanto, um teste usando-o poderia passar mesmo que o código real falhasse. autospec vai além e verifica as assinaturas dos métodos:

with patch("weather.WeatherClient", autospec=True) as Fake:
    client = Fake.return_value
    client.get_temperature("Pune", "extra", "args")
# TypeError: too many positional arguments

Prefira autospec=True ou create_autospec() ao zombar de suas próprias classes. Ele mantém as zombarias honestas à medida que o código real muda.

Zombando de datas e horas

O código que depende da data de hoje é difícil de testar. Envolva o relógio em uma pequena função e corrija isso:

def today():
    return datetime.date.today()

def is_weekend():
    return today().weekday() >= 5

@patch("weather.today")
def test_date(self, fake_today):
    fake_today.return_value = datetime.date(2026, 11, 7)
    print(weather.is_weekend())   # True (a Saturday)

Você não pode corrigir facilmente datetime.date.today diretamente, porque os tipos internos não permitem que seus atributos sejam definidos. Bibliotecas como freezegun e time-machine lidam com isso de forma mais completa.

Asserções em simulações

Método Cheques
assert_called() Liguei pelo menos uma vez
assert_called_once() Liguei exatamente uma vez
assert_called_with(...) A última chamada usou esses argumentos
assert_called_once_with(...) Exatamente uma chamada, com esses argumentos
assert_any_call(...) Qualquer chamada usou esses argumentos
assert_not_called() Nunca liguei

Quando uma afirmação falha, a mensagem é clara. Chamamos assert_called_once() em um método que nunca foi executado:

AssertionError: Expected 'save' to have been called once. Called 0 times.

Versões mais antigas do Python permitem que métodos de asserção com erros ortográficos, como assret_called_once, passem silenciosamente. Versões modernas geram um erro para nomes que começam com assert ou erros ortográficos comuns, mas uma especificação ainda é a proteção mais forte.

pytest e zombarias

Tudo acima também funciona em pytest. Muitos usuários do pytest também usam o fixture integrado monkeypatch para substituições simples, ou o plugin pytest-mock, que fornece um fixture mocker que desfaz patches automaticamente. A regra “patch onde é usado” se aplica a todos eles. Consulte nosso tutorial pytest para ver os acessórios e a configuração.

Quando não zombar

  • Não zombe do que você está testando. Se a simulação fizer todo o trabalho, o teste não prova nada.
  • Evite zombar de objetos de valor simples. Use objetos reais.
  • Não zombe de tudo. Testes cheios de simulações geralmente falham sempre que o código é refatorado, mesmo quando o comportamento está correto.
  • Mantenha alguns testes de integração reais. Os simulados codificam suas suposições sobre um serviço, e essas suposições podem estar erradas.

Os simulados também aumentam a cobertura do código facilmente, já que os caminhos simulados são executados sem qualquer comportamento real por trás deles.

Erros comuns de zombaria

  • Corrigindo o nome errado. Abordado acima, e ainda a causa número um de “minha simulação é ignorada”.
  • Esquecendo de definir return_value. O código em teste recebe um objeto Mock em vez de dados. Pode passar direto pela sua função e fazer um teste passar acidentalmente, por exemplo, quando você apenas verifica se um resultado é verdadeiro.
  • Afirmar na simulação em vez do resultado. Verificar se save() foi chamado é mais fraco do que verificar o que foi salvo. Use assert_called_once_with(...) com argumentos reais, ou melhor, afirme o resultado da função.
  • Vazamento de patches. Chamar patcher.start() sem stop() deixa o falso no lugar para testes posteriores. Use o gerenciador de contexto, o decorador ou addCleanup(patcher.stop).
  • Decoradores empilhados na ordem errada. Com vários @patch decoradores, o de baixo é passado como primeiro argumento. Misturá-los silenciosamente configura a simulação errada.
  • Simulador de uma biblioteca que você não possui em muitos testes. Envolva-o em uma pequena classe própria e zombe disso. Seus testes descrevem suas necessidades, não a API da biblioteca.

Simulações, stubs e falsificações

Essas palavras costumam ser usadas de maneira vaga, mas as diferenças ajudam na discussão dos testes. Um esboço apenas retorna respostas prontas. Um simulado também grava chamadas para que você possa verificar como ele foi usado. Um fake é uma implementação simplificada e funcional, como um repositório na memória em vez de um banco de dados. O Mock do Python pode atuar como um esboço ou uma simulação. Fakes são classes comuns que você mesmo escreve e geralmente levam a testes que sobrevivem melhor à refatoração, porque verificam o comportamento em vez de chamadas exatas.

Uma boa regra é zombar das bordas do seu sistema: a rede, o relógio, os números aleatórios, o sistema de arquivos e os serviços pagos de terceiros. Dentro dessas bordas, prefira objetos reais. Os testes construídos dessa forma permanecem rápidos e ainda exercitam o código que você realmente escreveu.

Considerações finais

Mocking é uma ferramenta precisa: substitua dependências lentas ou imprevisíveis, controle o que elas retornam e verifique como seu código as utilizou. Dois hábitos previnem a maioria dos problemas. Corrija o nome no módulo em teste e use a especificação automática para que suas simulações rejeitem chamadas que o objeto real rejeitaria.

Perguntas frequentes

O que é zombaria em Python?

A simulação substitui um objeto real, como um cliente API ou banco de dados, por um objeto falso que você controla durante um teste. Python inclui isso no módulo unittest.mock.

Qual é a diferença entre Mock e MagicMock?

MagicMock é um Mock que também suporta métodos mágicos como __len__, __iter__ e __enter__. patch() cria um MagicMock por padrão.

Por que meu patch não está funcionando?

Geralmente porque você corrigiu onde o objeto está definido em vez de onde ele é usado. Corrija o nome dentro do módulo em teste.

Qual é a diferença entre valor_de_retorno e efeito_lateral?

return_value define um resultado fixo. side_effect pode gerar uma exceção, retornar valores de uma lista em ordem ou chamar uma função.

O que a especificação automática faz?

Ele cria uma simulação a partir do objeto real, portanto, atributos ausentes e argumentos errados geram erros, assim como aconteceriam com o código real.

Continue aprendendo

Trabalhe com Stack Quora

Precisa de ajuda com testes, WordPress ou SEO?

Ajudamos as equipes a fornecer software melhor e a expandir seus sites. Conte-nos no que você está trabalhando e sugeriremos um próximo passo claro.

Continue lendo

Mais guias sobre testes, IA e desenvolvimento moderno

Gostou deste artigo? Escolha um tópico e vá direto para nossos tutoriais mais recentes.

Inteligência artificial

Explicação das alucinações de IA: por que os modelos de linguagem inventam as coisas e como reduzi-las

Explicação das alucinações de IA: o que são, por que os modelos de linguagem as produzem, tipos comuns, como reduzi-las com aterramento e instruções…

Leia o artigo →
DevOps

Variáveis de ambiente explicadas: arquivos .env, segredos e configuração bem feitos

Variáveis de ambiente explicadas com exemplos de Python que executamos: leitura e configuração, por que cada valor é uma string, arquivos .env, hera…

Leia o artigo →
Desenvolvimento de software

Explicação da limitação de taxa de API: algoritmos, cabeçalhos e tratamento de erros 429

Limitação de taxa de API explicada com uma simulação que executamos: token bucket, janela fixa, janela deslizante, 429 respostas, cabeçalhos de limi…

Leia o artigo →
Automação de testes

Zombando em Python: unittest.mock, patch e os erros que fazem os testes mentirem

Zombaria em Python com unittest.mock, testado: Mock e return_value, side_effect, patch onde um nome é usado, especificação e autospec, zombaria de d…

Leia o artigo →
Teste de software

Cobertura de código explicada: cobertura de linha versus filial e o que 100% realmente significa

Cobertura de código explicada com uma execução cobertura.py real: cobertura de linha, ramal e condição, leitura de um relatório, por que 100% de cob…

Leia o artigo →
Bancos de dados e SQL

DELETE vs TRUNCATE vs DROP em SQL: diferenças, reversão e quando usar cada um

DELETE vs TRUNCATE vs DROP explicado com SQL testado: o que cada um remove, rollback, aciona, redefinição de identidade, velocidade e como PostgreSQ…

Leia o artigo →
Bancos de dados e SQL

Exibições SQL explicadas: crie, use e evite as armadilhas, com exemplos

Visualizações SQL explicadas com consultas que executamos: criação de uma visualização, resultados ao vivo, ocultação de colunas, desempenho, visual…

Leia o artigo →
Desenvolvimento de software

Interface TypeScript vs tipo: diferenças reais, testadas com o compilador

Interface TypeScript versus tipo, verificada com o compilador real: o que ambos podem fazer, o que apenas um pode fazer, mesclagem de declarações, e…

Leia o artigo →
Desenvolvimento de software

Python *args e **kwargs explicados: funções flexíveis com exemplos claros

Python *args e **kwargs explicados com o código que executamos: empacotamento e descompactação, ordem dos parâmetros, argumentos somente de palavras…

Leia o artigo →
Desenvolvimento de software

Funções Python Lambda: sintaxe, usos reais e quando não usá-los

Funções lambda do Python explicadas com o código que executamos: sintaxe, classificação com chave, mapa e filtro, fechamentos, armadilha de ligação …

Leia o artigo →
Inteligência artificial

O que são agentes de IA? Como eles funcionam, com um loop de agente simples que você pode executar

O que são agentes de IA? Um guia simples para o loop do agente, ferramentas, memória e proteções, com um pequeno loop Python que executamos, riscos …

Leia o artigo →
DevOps

Monitoramento versus observabilidade: a verdadeira diferença, com logs, métricas e rastreamentos

Explicação do monitoramento versus observabilidade: falhas conhecidas e desconhecidas, logs, métricas e rastreamentos, um passo a passo do incidente…

Leia o artigo →
DevOps

Docker Volumes vs Bind Mounts: persista os dados do contêiner da maneira certa

Volumes Docker versus montagens de ligação explicadas: por que os dados do contêiner desaparecem, volumes nomeados, montagens de ligação, tmpfs, exe…

Leia o artigo →
Automação de testes

TDD vs BDD: principais diferenças, um recurso criado de ambas as maneiras e quando usar cada um

TDD vs BDD explicado com um recurso de desconto construído em ambos os sentidos: refatoração vermelho-verde com o código que executamos, cenários da…

Leia o artigo →
Desenvolvimento de software

Git merge vs rebase: diferenças, exemplos e quando usar cada um

Git merge vs rebase explicado com saída real do log, uma tabela comparativa, a regra de ouro do rebase e um fluxo de trabalho seguro para equipes.

Leia o artigo →
Desenvolvimento de software

List comprehension em Python: sintaxe, exemplos e quando evitar

List comprehension em Python explicada: sintaxe, filtros, if-else, laços aninhados, tempos medidos e quando um laço for comum é a melhor escolha.

Leia o artigo →
Bancos de dados e SQL

SQL JOIN explicado com exemplos (Inner, Left, Right, Full)

Entenda os joins SQL passo a passo com tabelas e consultas de exemplo: INNER, LEFT, RIGHT, FULL, CROSS e SELF, além de erros comuns e dicas de desem…

Leia o artigo →