
Passo 1 de 7
Definir a fronteira do teste
Delimite as regras sob teste e reconheça quais colaboradores devem ficar fora do cenário isolado.
Trilha de aprendizado · Nível 8 · Tutorial 4
Delimitar testes unitários e substituir colaboradores externos por objetos controlados, tornando os cenários reproduzíveis sem acessar recursos reais.
Definir a fronteira do teste
Delimite as regras sob teste e reconheça quais colaboradores devem ficar fora do cenário isolado. 2 min
Fornecer respostas com um stub manual
Construa um substituto simples para controlar as respostas do repositório e testar os cenários de inscrição nova e duplicada sem acessar recursos externos. 3 min
Simular falhas sem provocar problemas reais
Configure uma falha determinística no repositório e verifique a reação do serviço por sua operação pública. 3 min
Representar estado com um fake em memória
Crie um repositório simplificado que mantém inscrições em memória e permite observar como uma gravação afeta consultas posteriores. 3 min
Registrar interações que fazem parte do contrato
Registre chamadas essenciais em um substituto manual para verificar qual participante foi enviado ao repositório e quando nenhuma gravação deve ocorrer. 3 min
Reconhecer o que o isolamento garante
Avalie o determinismo, o acoplamento e os limites de confiança de testes que usam substitutos manuais. 3 min
Consolidar uma pequena suíte isolada
Reúna o serviço e um substituto manual configurável em três testes reproduzíveis: sucesso, duplicidade e falha externa. 4 min

Passo 1 de 7
Delimite as regras sob teste e reconheça quais colaboradores devem ficar fora do cenário isolado.
Nosso caso contínuo será um serviço de inscrição em uma oficina. Ele consulta um repositório, rejeita uma inscrição duplicada e, quando o participante ainda não está inscrito, solicita o registro antes de confirmar o sucesso.
A fronteira do teste indica o comportamento que queremos verificar. Neste caso, as decisões de rejeitar ou confirmar pertencem ao serviço. Consultar e armazenar inscrições são responsabilidades do repositório, um objeto colaborador.
Em um teste unitário isolado do serviço, mantemos suas regras dentro da fronteira e colocamos o repositório real fora dela, fornecendo um substituto controlado.
Compare as duas configurações. A fronteira do serviço permanece igual; o que muda é o colaborador exercitado pelo teste.

À esquerda, o teste atravessa a fronteira e usa infraestrutura real. À direita, exercita as regras do serviço com respostas controladas.
Um teste é unitário isolado quando verifica a unidade escolhida sem acionar colaboradores externos reais. Um teste é de integração quando também exercita a comunicação com componentes reais, como um repositório que acessa arquivos, banco de dados ou serviços.
A classificação não depende da quantidade de classes, do tamanho da função de teste nem do uso de pytest. Ela depende da fronteira escolhida e dos componentes que realmente são executados.
Exemplo
Cenário A: o serviço recebe um objeto controlado que informa que o participante ainda não existe. O teste verifica que o serviço confirma a inscrição. O repositório real ficou fora da fronteira: é uma verificação isolada.
Cenário B: o serviço recebe o repositório real, que abre um arquivo ou consulta um serviço externo. A verificação atravessa a fronteira entre os componentes: é um teste de integração.
Recursos externos podem variar conforme dados, permissões, rede ou disponibilidade. Também podem tornar o teste lento e causar efeitos colaterais. Isso não os torna dispensáveis, mas justifica excluí-los quando o objetivo é testar somente as regras do serviço.
Um teste fornece ao serviço de inscrição um objeto controlado que responde “participante inexistente”. Em seguida, chama a operação pública do serviço e verifica a confirmação da inscrição. Qual classificação e fronteira descrevem melhor esse cenário?

Passo 2 de 7
Construa um substituto simples para controlar as respostas do repositório e testar os cenários de inscrição nova e duplicada sem acessar recursos externos.
O serviço de inscrição usa somente duas operações do repositório:
existe(email) informa com True ou False se o participante já está inscrito;registrar(email) armazena uma inscrição nova e não precisa devolver um valor.Um stub manual pode atender a esse contrato com uma classe simples, sem herdar da implementação real. Sua resposta é escolhida durante a preparação do teste e permanece previsível.
O serviço recebe o stub pelo construtor e usa seus métodos como usaria os do repositório real.

O teste troca apenas o colaborador. A decisão de aceitar ou rejeitar a inscrição continua no serviço.
Em uma pasta nova, crie o arquivo inscricoes.py. O serviço recebe qualquer objeto que ofereça os métodos esperados. Ele não importa nem cria um repositório específico.
A regra de duplicidade pertence ao serviço, não ao substituto.
class ServicoInscricao:
def __init__(self, repositorio):
self.repositorio = repositorio
def inscrever(self, email):
if self.repositorio.existe(email):
return False
self.repositorio.registrar(email)
return True
Na mesma pasta, crie test_inscricoes.py. O stub devolve o valor recebido em seu construtor. Como o efeito de armazenamento ainda não é o foco, registrar pode não fazer nada.
Cada teste cria seu próprio stub e o fornece explicitamente ao serviço.
from inscricoes import ServicoInscricao
class StubRepositorio:
def __init__(self, resposta_existe):
self.resposta_existe = resposta_existe
def existe(self, email):
return self.resposta_existe
def registrar(self, email):
pass
def test_aceita_inscricao_nova():
repositorio = StubRepositorio(resposta_existe=False)
servico = ServicoInscricao(repositorio)
resultado = servico.inscrever("ana@example.com")
assert resultado is True
def test_rejeita_inscricao_duplicada():
repositorio = StubRepositorio(resposta_existe=True)
servico = ServicoInscricao(repositorio)
resultado = servico.inscrever("ana@example.com")
assert resultado is False
Com o ambiente que contém o pytest ativado, execute o comando a partir da pasta dos dois arquivos.
python -m pytest -qDica
O stub apenas responde se a inscrição existe. Quem transforma essa informação em aceitação ou rejeição é ServicoInscricao. Colocar a regra de duplicidade dentro do stub faria o teste reproduzir indevidamente a lógica que deveria verificar.
Para representar uma inscrição já existente, complete a configuração: StubRepositorio(resposta_existe=____)
Execute a suíte e registre o que apareceu no terminal. Explique também o que resposta_existe=False e resposta_existe=True representaram nos dois testes.
Escreva pelo menos 40 caracteres (0/40).

Passo 3 de 7
Configure uma falha determinística no repositório e verifique a reação do serviço por sua operação pública.
Uma inscrição duplicada é rejeitada por uma regra do próprio serviço. Já uma ConnectionError informa que o repositório não conseguiu atender à consulta.
Para testar essa segunda situação, o stub pode lançar sempre a exceção esperada. Assim, não é necessário desligar serviços, alterar permissões ou manipular arquivos. Neste contrato, o serviço deve propagar a ConnectionError; portanto, a chamada não chega a confirmar sucesso.
A falha nasce no colaborador controlado e atravessa a operação pública do serviço. O caminho de sucesso é interrompido.

O teste aciona o serviço; o stub apenas determina onde a falha externa surgirá.
O pytest.raises envolve servico.inscrever(...), e não uma chamada direta ao stub. Desse modo, o teste verifica como o serviço reage à falha do colaborador.
import pytest
class ServicoInscricao:
def __init__(self, repositorio):
self.repositorio = repositorio
def inscrever(self, email):
if self.repositorio.existe(email):
return False
self.repositorio.registrar(email)
return True
class RepositorioIndisponivelStub:
def existe(self, email):
raise ConnectionError("repositório indisponível")
def registrar(self, email):
pass
def test_propaga_falha_ao_consultar_repositorio():
repositorio = RepositorioIndisponivelStub()
servico = ServicoInscricao(repositorio)
with pytest.raises(ConnectionError):
servico.inscrever("ana@example.com")
Para verificar a reação do serviço, basta colocar repositorio.existe(...) diretamente dentro de pytest.raises.
Por que um stub que lança ConnectionError é mais adequado a este teste unitário do que provocar a indisponibilidade de um recurso real?
Escreva pelo menos 40 caracteres (0/40).

Passo 4 de 7
Crie um repositório simplificado que mantém inscrições em memória e permite observar como uma gravação afeta consultas posteriores.
Um stub devolve respostas escolhidas antecipadamente. Isso funciona quando cada consulta pode receber uma resposta fixa.
Porém, alguns cenários exigem estado: depois que o serviço registra uma inscrição, uma consulta posterior deve encontrá-la. Nesse caso, podemos usar um fake — uma implementação funcional simplificada do repositório, sem banco de dados, arquivos ou serviços externos.
O fake mantém apenas o comportamento necessário ao teste. Ele armazena e consulta inscrições; a regra de rejeitar duplicidades continua pertencendo ao serviço.

À esquerda, o stub entrega uma resposta predeterminada. À direita, o fake calcula a resposta consultando seu estado em memória.
O serviço continua chamando existe(email) e registrar(email). O fake oferece esses mesmos métodos, mas usa um conjunto de instância. Assim, cada objeto começa com sua própria coleção e não compartilha inscrições com outros testes.
Salve este conteúdo como test_inscricoes.py.
class ServicoInscricao:
def __init__(self, repositorio):
self.repositorio = repositorio
def inscrever(self, email):
if self.repositorio.existe(email):
return False
self.repositorio.registrar(email)
return True
class RepositorioInscricoesEmMemoria:
def __init__(self):
self._inscricoes = set()
def existe(self, email):
return email in self._inscricoes
def registrar(self, email):
self._inscricoes.add(email)
def test_inscricao_registrada_passa_a_ser_encontrada():
repositorio = RepositorioInscricoesEmMemoria()
servico = ServicoInscricao(repositorio)
resultado = servico.inscrever("ana@example.com")
assert resultado is True
assert repositorio.existe("ana@example.com") is True
def test_segunda_inscricao_do_mesmo_email_e_rejeitada():
repositorio = RepositorioInscricoesEmMemoria()
servico = ServicoInscricao(repositorio)
primeiro_resultado = servico.inscrever("ana@example.com")
segundo_resultado = servico.inscrever("ana@example.com")
assert primeiro_resultado is True
assert segundo_resultado is False
def test_instancias_nao_compartilham_inscricoes():
primeiro_repositorio = RepositorioInscricoesEmMemoria()
segundo_repositorio = RepositorioInscricoesEmMemoria()
primeiro_repositorio.registrar("ana@example.com")
assert primeiro_repositorio.existe("ana@example.com") is True
assert segundo_repositorio.existe("ana@example.com") is False
Dica
Os testes consultam repositorio.existe(...) em vez de acessar _inscricoes. Isso verifica o efeito observável e permite mudar a coleção interna sem reescrever a expectativa do teste.
Associe cada necessidade ao componente mais adequado.
Toque em um item e depois no par correspondente.
No seu computador, execute o arquivo com:
python -m pytest test_inscricoes.py -v
Os três testes devem passar. Depois, observe por que _inscricoes precisa ser criada dentro de __init__: cada instância recebe um conjunto novo, evitando que dados de um teste apareçam em outro.
Execute os testes e explique como eles demonstram que uma gravação influencia consultas posteriores sem vazar estado entre instâncias.
Escreva pelo menos 80 caracteres (0/80).

Passo 5 de 7
Registre chamadas essenciais em um substituto manual para verificar qual participante foi enviado ao repositório e quando nenhuma gravação deve ocorrer.
O retorno de inscrever() informa se a inscrição foi aceita, mas não comprova que o participante correto foi enviado ao repositório. Quando essa gravação faz parte do contrato, o substituto pode guardar cada argumento recebido em uma lista de instância.
O teste observa essa lista para confirmar dois comportamentos: no sucesso, o serviço solicita o registro correto; na duplicidade ou na falha da consulta, não solicita registro algum.
O teste verifica tanto o resultado público do serviço quanto o registro mantido pelo substituto.

A lista de chamadas torna observável o argumento enviado ao colaborador, sem acessar um repositório real.
Crie inscricoes.py com o serviço e o substituto abaixo. RepositorioRegistrador.existe() fornece uma resposta ou falha predeterminada, enquanto registrar() preserva os participantes recebidos em registros.
Um substituto pode cumprir mais de um papel útil ao cenário; não é necessário encaixá-lo em uma categoria rígida.
O serviço mantém a regra de duplicidade. O substituto apenas responde à consulta e registra chamadas.
class ServicoInscricao:
def __init__(self, repositorio):
self.repositorio = repositorio
def inscrever(self, participante):
if self.repositorio.existe(participante):
return False
self.repositorio.registrar(participante)
return True
class RepositorioRegistrador:
def __init__(self, ja_existe=False, falha_consulta=False):
self.ja_existe = ja_existe
self.falha_consulta = falha_consulta
self.registros = []
def existe(self, participante):
if self.falha_consulta:
raise ConnectionError("repositório indisponível")
return self.ja_existe
def registrar(self, participante):
self.registros.append(participante)
Dica
registros é criada em __init__, portanto cada instância começa com sua própria lista. Isso evita que chamadas de um teste apareçam em outro.
Crie tests/test_interacoes.py. Os três testes chamam inscrever(), e não os métodos do substituto diretamente. Depois, execute python -m pytest na pasta do projeto.
As verificações cobrem o argumento enviado no sucesso e a ausência de gravação nos dois caminhos interrompidos.
import pytest
from inscricoes import RepositorioRegistrador, ServicoInscricao
def test_inscricao_nova_registra_o_participante_correto():
repositorio = RepositorioRegistrador(ja_existe=False)
servico = ServicoInscricao(repositorio)
resultado = servico.inscrever("Ana")
assert resultado is True
assert repositorio.registros == ["Ana"]
def test_inscricao_duplicada_nao_solicita_novo_registro():
repositorio = RepositorioRegistrador(ja_existe=True)
servico = ServicoInscricao(repositorio)
resultado = servico.inscrever("Ana")
assert resultado is False
assert repositorio.registros == []
def test_falha_na_consulta_nao_solicita_registro():
repositorio = RepositorioRegistrador(falha_consulta=True)
servico = ServicoInscricao(repositorio)
with pytest.raises(ConnectionError):
servico.inscrever("Ana")
assert repositorio.registros == []
Qual conjunto de verificações confirma as interações essenciais sem acoplar o teste a detalhes internos irrelevantes?

Passo 6 de 7
Avalie o determinismo, o acoplamento e os limites de confiança de testes que usam substitutos manuais.
Um teste isolado deve produzir o mesmo resultado sempre que receber a mesma preparação. Para isso, respostas, falhas e estado inicial do substituto precisam estar explícitos. Também não pode haver acesso externo oculto nem uma coleção compartilhada entre testes.
Criar um substituto novo em cada teste ajuda a impedir que uma execução influencie a seguinte.
O serviço pode receber o substituto mais simples que torne o comportamento relevante observável.

Resposta fixa, estado em memória e registro de chamadas fornecem evidências diferentes, mas nenhum deles exercita a infraestrutura real.
Exemplo
Adicionar estado ou registros que o cenário não usa aumenta a complexidade sem ampliar a evidência.
Atenção
Um teste fica frágil quando exige, por exemplo, exatamente uma consulta ao repositório, embora o contrato só determine que duplicidades sejam rejeitadas. Uma otimização poderia alterar a quantidade de chamadas sem mudar o comportamento público.
Verifique interações apenas quando elas forem efeitos exigidos pelo contrato, como não registrar uma inscrição duplicada.
A compatibilidade com o repositório real inclui os argumentos aceitos, os resultados devolvidos, as exceções produzidas e os efeitos esperados. Um substituto pode permitir que todos os testes passem mesmo representando esse contrato de forma incorreta.
Além disso, testes isolados não comprovam que dados serão realmente persistidos, que a comunicação externa funcionará ou que o colaborador real se comportará como o substituto. Testes de integração complementam essa confiança.
Dica
Um teste isolado bem-sucedido sustenta uma conclusão limitada: dadas as respostas configuradas para o colaborador, a regra do serviço apresentou o comportamento esperado.
Considere este fake usado em um teste do serviço:
class RepositorioEmMemoria:
inscricoes = set()
def existe(self, email):
return email in self.inscricoes
def registrar(self, email):
self.inscricoes.add(email)
O teste passa ao inscrever uma pessoa nova. Aponte uma fonte de variação ou fragilidade nesse fake e explique qual risco de compatibilidade com o repositório real ainda permanece mesmo que o teste passe.
Escreva pelo menos 80 caracteres (0/80).

Passo 7 de 7
Reúna o serviço e um substituto manual configurável em três testes reproduzíveis: sucesso, duplicidade e falha externa.
Crie uma pasta para a prática e, dentro dela, o arquivo inscricoes.py. O serviço contém as regras que queremos verificar: rejeitar duplicidade, solicitar o registro de uma inscrição nova e propagar uma falha de conexão. O repositório chega pelo construtor, por isso cada teste poderá fornecer um objeto controlado.
Salve este conteúdo no arquivo inscricoes.py:
class InscricaoDuplicada(Exception):
pass
class ServicoInscricao:
def __init__(self, repositorio):
self.repositorio = repositorio
def inscrever(self, email):
if self.repositorio.existe(email):
raise InscricaoDuplicada(email)
self.repositorio.registrar(email)
return True
Crie a pasta tests e nela o arquivo test_inscricoes.py. O RepositorioControlado fornece uma resposta ou falha predeterminada e registra os argumentos enviados a registrar. Ele não reproduz um banco de dados nem a regra de duplicidade do serviço.
Os três testes criam objetos novos e seguem preparação, execução e verificação:
import pytest
from inscricoes import InscricaoDuplicada, ServicoInscricao
class RepositorioControlado:
def __init__(self, existe=False, erro_consulta=None):
self.existe_resultado = existe
self.erro_consulta = erro_consulta
self.registros = []
def existe(self, email):
if self.erro_consulta is not None:
raise self.erro_consulta
return self.existe_resultado
def registrar(self, email):
self.registros.append(email)
def test_confirma_inscricao_nova_e_solicita_registro():
repositorio = RepositorioControlado(existe=False)
servico = ServicoInscricao(repositorio)
resultado = servico.inscrever("ana@example.com")
assert resultado is True
assert repositorio.registros == ["ana@example.com"]
def test_rejeita_duplicidade_sem_solicitar_novo_registro():
repositorio = RepositorioControlado(existe=True)
servico = ServicoInscricao(repositorio)
with pytest.raises(InscricaoDuplicada):
servico.inscrever("ana@example.com")
assert repositorio.registros == []
def test_propaga_falha_de_consulta_sem_solicitar_registro():
repositorio = RepositorioControlado(
erro_consulta=ConnectionError("repositório indisponível")
)
servico = ServicoInscricao(repositorio)
with pytest.raises(ConnectionError):
servico.inscrever("ana@example.com")
assert repositorio.registros == []
Dica
A resposta fixa permite representar inscrição nova ou duplicada; a exceção configurada representa indisponibilidade; e a lista permite observar o pedido de registro. Esses papéis podem coexistir em uma classe simples quando isso deixa o teste claro.
Abra o terminal na pasta que contém inscricoes.py e a pasta tests. Execute a suíte com o mesmo ambiente em que o pytest está instalado. Se algum teste falhar, leia a diferença apresentada antes de alterar o código.
Execute a suíte a partir da raiz da prática:
python -m pytest -qDepois de executar, relate: quais cenários passaram; por que a resposta, a falha e o registro de chamadas foram suficientes; e uma garantia que essa suíte isolada não oferece.
Escreva pelo menos 100 caracteres (0/100).
Resumo
A confiança obtida está delimitada pela fronteira escolhida.
Parabéns! Você concluiu: Isolar dependências com substitutos de teste
50 XP
Milhares de cursos online em vídeo, ebooks e áudiobooks.
Para testar seus conhecimentos no decorrer dos cursos online
Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail
Baixe nosso aplicativo pelo QR Code ou pelos links abaixo:.
+ de 10 milhões
de alunos
Certificado grátis e
válido em todo o Brasil
60 mil exercícios
gratuitos
4,8/5 classificação
nas lojas de apps
Cursos gratuitos em
vídeo, ebooks e audiobooks