unittest.mock substitui colaboradores durante um teste e registra como foram usados. O recurso é útil para relógio, gateway ou cliente externo, mas excesso de mocks prende a suíte à implementação.

Aplicar patch no namespace correto

Se servico.py faz from gateway import enviar, o teste deve alterar servico.enviar, pois é ali que a função será procurada:

from unittest.mock import patch

from app.servico import processar


@patch("app.servico.enviar", autospec=True)
def test_processar_envia_evento(enviar_mock) -> None:
    enviar_mock.return_value = {"id": "evt-7"}

    resultado = processar({"pedido_id": 7})

    assert resultado == "evt-7"
    enviar_mock.assert_called_once_with({"pedido_id": 7})

autospec=True ajuda a detectar argumentos incompatíveis. Use um context manager quando o patch só for necessário em parte do teste e garanta que ele seja desfeito automaticamente.

Retorno, erro e sequência

return_value define sucesso; side_effect pode lançar uma exceção ou fornecer resultados sucessivos. Cubra apenas falhas que a aplicação realmente trata. Um mock que aceita qualquer atributo pode esconder erro de digitação, por isso uma spec é preferível.

Mock, MagicMock e contratos

Mock cria atributos sob demanda e registra chamadas. MagicMock também fornece métodos especiais, como __enter__, __iter__ e __len__, sendo apropriado quando o colaborador participa de um protocolo da linguagem. Não escolha MagicMock apenas por conveniência: métodos mágicos disponíveis sem intenção podem permitir que um teste passe com um objeto irreal.

Passe uma classe ou instância em spec para restringir os atributos acessíveis. spec_set é ainda mais rígido e impede atribuir nomes inexistentes. Já create_autospec() constrói um mock com a assinatura do objeto original:

from unittest.mock import create_autospec


class Gateway:
    def cobrar(self, pedido_id: int, valor: int) -> str:
        ...


gateway = create_autospec(Gateway, instance=True, spec_set=True)
gateway.cobrar.return_value = "pag-42"

assert gateway.cobrar(42, 1990) == "pag-42"
gateway.cobrar.assert_called_once_with(42, 1990)

Esse controle detecta atributos digitados incorretamente e várias mudanças de assinatura. Ele não executa validações internas nem garante que os valores tenham os tipos anotados. Um teste de integração ainda é necessário para provar que o código conversa corretamente com a implementação real.

Modele respostas e falhas com side_effect

Uma exceção em side_effect permite testar um caminho de erro sem provocar uma falha externa real. Uma lista produz respostas sucessivas e levanta StopIteration quando acaba:

from unittest.mock import Mock

consultar = Mock(side_effect=[TimeoutError, {"status": "ok"}])

try:
    consultar()
except TimeoutError:
    pass

assert consultar() == {"status": "ok"}
assert consultar.call_count == 2

Uma função também pode ser usada como side_effect para calcular a resposta a partir dos argumentos. Mantenha essa função pequena. Se ela reproduzir toda a lógica da dependência, o teste passa a manter uma segunda implementação, que pode errar da mesma maneira ou divergir silenciosamente.

Use wraps=objeto_real quando quiser registrar chamadas e delegar a execução ao objeto. Isso é um spy parcial, não isolamento. Efeitos reais continuam acontecendo, portanto não envolva acidentalmente clientes de rede, escrita em disco ou relógios não controlados.

Verifique chamadas sem acoplar a ordem interna

Asserções como assert_called_once_with() comunicam bem um contrato de interação. Para várias chamadas, compare call_args_list com objetos call:

from unittest.mock import Mock, call

publicar = Mock()
publicar("pedido.criado", {"id": 1})
publicar("pedido.criado", {"id": 2})

assert publicar.call_args_list == [
    call("pedido.criado", {"id": 1}),
    call("pedido.criado", {"id": 2}),
]

Se a ordem não fizer parte do contrato, use assert_has_calls(..., any_order=True) ou compare uma representação independente de ordem. Não afirme cada chamada auxiliar apenas porque está disponível. Um refactor que preserva o resultado deveria continuar passando; caso contrário, a suíte se torna um obstáculo para melhorar o design.

ANY representa um argumento deliberadamente variável, como um identificador gerado. Use-o somente naquele campo e confirme separadamente propriedades importantes do valor capturado em call_args. Aceitar tudo reduz a capacidade do teste de detectar regressões.

Patch como decorator, contexto ou objeto

O decorator dura durante toda a função de teste. O context manager limita claramente a substituição a um bloco. patch.object() é útil quando você já possui o objeto ou classe. patch.dict() altera temporariamente um dicionário, inclusive os.environ, e restaura seu estado:

import os
from unittest.mock import patch

with patch.dict(os.environ, {"MODO": "teste"}, clear=False):
    assert os.environ["MODO"] == "teste"

Evite iniciar um patch manualmente com start() sem registrar a limpeza. Se isso for necessário em unittest.TestCase, associe patcher.stop a addCleanup() imediatamente. Um patch que vaza para outro teste produz falhas dependentes de ordem e difíceis de reproduzir.

O ponto mais importante continua sendo o namespace de consulta. Se app.servico importou enviar diretamente, trocar gateway.enviar depois não altera a referência já ligada em app.servico. Desenhe a importação no papel quando um patch aparentemente correto não tem efeito.

Métodos assíncronos e context managers

AsyncMock representa uma função aguardável. Desde versões modernas do Python, patch() escolhe AsyncMock automaticamente ao substituir uma função assíncrona:

from unittest.mock import AsyncMock

buscar = AsyncMock(return_value={"id": 7})
resultado = await buscar(7)

assert resultado == {"id": 7}
buscar.assert_awaited_once_with(7)

Use assert_awaited*, não apenas assert_called*, quando a espera faz parte do comportamento. Para um context manager assíncrono, configure __aenter__ e __aexit__ explicitamente. Isso deixa claro qual objeto o bloco async with recebe.

Prefira limites arquiteturais estáveis

Mocks funcionam melhor em fronteiras: relógio, gerador de identificador, fila, gateway de pagamento ou cliente de API. Dentro do domínio, objetos reais e funções puras normalmente produzem testes mais legíveis. Um fake em memória pode representar um repositório com comportamento consistente e permitir várias operações sem dezenas de configurações.

Não simule uma biblioteca inteira. Encapsule o cliente de terceiros atrás de uma interface pequena controlada pela aplicação e simule essa interface. Mantenha alguns testes de integração para verificar serialização, autenticação, timeouts e mudanças da API real. A combinação evita tanto testes lentos em excesso quanto uma suíte que só confirma as próprias expectativas.

Lista de revisão

Antes de aceitar um teste, pergunte se a dependência realmente precisa ser isolada, se o patch atua onde o nome é consultado e se uma spec protege o contrato. Confirme que o teste cobre saída ou efeito observável, que falhas modeladas são tratadas pela aplicação e que o patch sempre é restaurado. Por fim, execute também testes reais da fronteira em uma camada adequada.

Configure propriedades e objetos encadeados com moderação

Uma propriedade exige PropertyMock ligado ao tipo do mock, não à instância. Para objetos encadeados, é possível configurar cliente.sessao().enviar.return_value, mas uma cadeia longa costuma revelar uma interface difícil de usar. Extraia um adaptador pequeno em vez de ensinar todos os testes sobre a estrutura interna de uma biblioteca.

Para conferir chamadas encadeadas, mock_calls e call.call_list() representam a sequência completa. Use esse recurso quando a sequência for realmente o protocolo. Se o resultado final basta, uma assertion de estado tende a resistir melhor a refactors.

Sentinelas e identidade

sentinel cria objetos únicos e legíveis para argumentos cujo valor específico não importa, mas cuja identidade precisa ser preservada. É mais claro do que object() repetido:

from unittest.mock import Mock, sentinel

armazenar = Mock()
armazenar(sentinel.conexao)

armazenar.assert_called_once_with(sentinel.conexao)

Para objetos mutáveis, lembre que o mock guarda referências aos argumentos. Se o código modificar uma lista depois da chamada, call_args refletirá o valor posterior. Quando o estado no momento da chamada é o contrato, copie o argumento em um side_effect ou melhore a fronteira para receber um valor imutável.

Reset não substitui isolamento

reset_mock() apaga histórico de chamadas e, por padrão, conserva return_value e side_effect. Embora seja útil em uma verificação com fases explícitas, reutilizar o mesmo mock entre testes cria estado compartilhado. Prefira uma instância nova por teste e fixtures com escopo curto.

Também evite assertions negativas muito amplas. assert_not_called() prova apenas que aquele mock não foi chamado. Ele não demonstra ausência de todos os efeitos externos, principalmente quando o alvo errado foi substituído. Combine a assertion com um resultado observável e uma spec que faça o teste falhar se o código procurar um atributo inesperado.

Diferencie dublês

Um stub devolve respostas preparadas; um spy observa chamadas; um fake oferece uma implementação simplificada; um mock, no sentido estrito, verifica expectativas de interação. A biblioteca unittest.mock pode construir vários desses dublês. Nomear o papel ajuda a escolher a técnica e evita configurar expectativas que o teste não precisa.

Para uma regra de negócio pura, use valores reais. Para um repositório com várias operações coerentes, um fake pode ser mais expressivo. Para confirmar que um evento obrigatório foi publicado uma vez, um mock com spec é adequado. Para uma API externa, combine o adaptador simulado com testes de contrato ou integração.

Antes de criar mocks, veja se uma função pura ou objeto fake pequeno comunica melhor a intenção. O artigo de fixtures e monkeypatch no pytest mostra alternativas, e RESPX é mais expressivo para HTTPX.

A documentação oficial de unittest.mock, consultada em 28 de julho de 2026, explica patch, spec, chamadas e mocks assíncronos. Prefira assertions sobre a saída e efeitos do sistema; verifique chamadas internas apenas quando elas fizerem parte do contrato.