"Qual a diferença entre mock e stub?" é uma das perguntas técnicas que mais separa quem decora de quem entende. Na prática do dia a dia, todo teste de unidade isola a peça sob teste das suas dependências — banco, API externa, relógio, e-mail — e o faz com test doubles (dublês de teste). O termo guarda-chuva é de Gerard Meszaros, e existem cinco tipos, do mais bobo ao mais esperto.
O espectro dos dublês
só ocupa espaço ──────────────────────► verifica interação
DUMMY → STUB → FAKE → SPY → MOCK
DUMMY passado mas nunca usado (preenche um parâmetro)
STUB devolve respostas prontas ("quando perguntarem X, responda Y")
FAKE implementação real, porém simplificada (ex.: banco em memória)
SPY como o stub, mas grava como foi chamado (para checar depois)
MOCK já vem com a expectativa do que DEVE ser chamado (falha se não for)
Dummy — só para preencher
Um objeto passado apenas para satisfazer a assinatura, nunca de fato usado no caminho testado. Ex.: um logger exigido pelo construtor, mas irrelevante para o que estou verificando.
const loggerDummy = {};
const servico = new Servico(loggerDummy); // só para construir
Stub — respostas prontas
O stub controla a entrada indireta: ele força a dependência a devolver um valor fixo, para eu testar como meu código reage. Não verifico como foi chamado — só uso a resposta.
// Quero testar a regra de frete grátis sem chamar a API real de CEP
const apiCepStub = {
consultar: () => ({ regiao: 'sudeste', taxa: 0 })
};
const total = calcularEntrega(apiCepStub, carrinho);
expect(total.freteGratis).toBe(true); // verifico ESTADO
Fake — implementação real, porém leve
O fake tem lógica de verdade, só que simplificada e inadequada para produção. O exemplo clássico é um repositório que guarda dados num array em memória em vez de bater no banco — rápido e sem efeito colateral.
class RepositorioFake {
constructor() { this.itens = []; }
salvar(item) { this.itens.push(item); }
buscarPorId(id) { return this.itens.find(i => i.id === id); }
}
Spy — grava o que aconteceu
O spy embrulha uma chamada e registra como ela foi usada — quantas vezes, com quais argumentos — para eu inspecionar depois da ação. É o meio-termo: não impõe expectativa antes, mas permite verificar comportamento.
const emailSpy = jest.fn();
notificarUsuario(usuario, emailSpy);
// verifico COMPORTAMENTO: foi chamado certo?
expect(emailSpy).toHaveBeenCalledTimes(1);
expect(emailSpy).toHaveBeenCalledWith('ana@exemplo.com');
Mock — expectativa embutida
O mock é o mais opinativo: você define antes o que ele espera receber, e o próprio mock falha o teste se a interação esperada não acontecer. Ele verifica comportamento por construção.
const gateway = mock(PagamentoGateway);
when(gateway.cobrar(100)).thenReturn({ ok: true });
finalizarCompra(gateway, 100);
// o mock exige que cobrar(100) tenha sido chamado exatamente assim
verify(gateway).cobrar(100);
A distinção que importa: estado vs comportamento
Mais útil que decorar os cinco nomes é entender a divisão que eles revelam:
| Verifica… | Dublê típico | Pergunta do teste |
|---|---|---|
| Estado (resultado) | Stub, Fake | "Dado este retorno, meu código produziu a saída certa?" |
| Comportamento (interação) | Spy, Mock | "Meu código chamou a dependência do jeito esperado?" |
Stub responde, mock cobra. Stub está a serviço de uma verificação de estado; mock é a verificação — de comportamento. Confundir os dois leva a testes que verificam a coisa errada.
Cuidado com o excesso de mocks
Dublês demais acoplam o teste à implementação: você passa a testar "o método X chamou o método Y" em vez do comportamento observável. Aí qualquer refatoração quebra os testes mesmo sem bug. A regra prática é dublar o que é lento, instável ou tem efeito colateral (rede, banco, hora, e-mail) e deixar a lógica de negócio rodar de verdade.
Conclusão
Dummy preenche, stub responde, fake implementa leve, spy grava e mock cobra. Saber qual usar não é trivia — é o que mantém seus testes unitários rápidos, isolados e focados no que realmente importa: o comportamento do código, não os detalhes de quem ele chama.
"What's the difference between a mock and a stub?" is one of those technical questions that best separates people who memorize from people who understand. In day-to-day practice, every unit test isolates the piece under test from its dependencies — database, external API, clock, email — and does so with test doubles. The umbrella term comes from Gerard Meszaros, and there are five kinds, from the dumbest to the smartest.
The spectrum of doubles
just takes up space ──────────────────────► verifies interaction
DUMMY → STUB → FAKE → SPY → MOCK
DUMMY passed around but never used (fills a parameter)
STUB returns canned answers ("when asked X, answer Y")
FAKE a real implementation, but simplified (e.g. in-memory database)
SPY like a stub, but records how it was called (to check later)
MOCK comes with the expectation of what MUST be called (fails if not)
Dummy — just to fill the slot
An object passed only to satisfy the signature, never actually used on the tested path. E.g. a logger required by the constructor, but irrelevant to what I'm verifying.
const loggerDummy = {};
const servico = new Servico(loggerDummy); // just to build it
Stub — canned answers
The stub controls indirect input: it forces the dependency to return a fixed value, so I can test how my code reacts. I don't verify how it was called — I just use the answer.
// I want to test the free-shipping rule without calling the real ZIP code API
const apiCepStub = {
consultar: () => ({ regiao: 'sudeste', taxa: 0 })
};
const total = calcularEntrega(apiCepStub, carrinho);
expect(total.freteGratis).toBe(true); // I verify STATE
Fake — a real but lightweight implementation
The fake has real logic, just simplified and unsuitable for production. The classic example is a repository that keeps data in an in-memory array instead of hitting the database — fast and with no side effects.
class RepositorioFake {
constructor() { this.itens = []; }
salvar(item) { this.itens.push(item); }
buscarPorId(id) { return this.itens.find(i => i.id === id); }
}
Spy — records what happened
The spy wraps a call and records how it was used — how many times, with which arguments — so I can inspect it after the action. It's the middle ground: it sets no expectation up front, but lets me verify behavior.
const emailSpy = jest.fn();
notificarUsuario(usuario, emailSpy);
// I verify BEHAVIOR: was it called correctly?
expect(emailSpy).toHaveBeenCalledTimes(1);
expect(emailSpy).toHaveBeenCalledWith('ana@exemplo.com');
Mock — built-in expectation
The mock is the most opinionated: you define up front what it expects to receive, and the mock itself fails the test if the expected interaction doesn't happen. It verifies behavior by construction.
const gateway = mock(PagamentoGateway);
when(gateway.cobrar(100)).thenReturn({ ok: true });
finalizarCompra(gateway, 100);
// the mock requires that cobrar(100) was called exactly like this
verify(gateway).cobrar(100);
The distinction that matters: state vs behavior
More useful than memorizing the five names is understanding the split they reveal:
| Verifies… | Typical double | The test's question |
|---|---|---|
| State (result) | Stub, Fake | "Given this return value, did my code produce the right output?" |
| Behavior (interaction) | Spy, Mock | "Did my code call the dependency the way I expected?" |
A stub answers, a mock demands. A stub serves a state verification; a mock is the verification — of behavior. Confusing the two leads to tests that verify the wrong thing.
Beware of too many mocks
Too many doubles couple the test to the implementation: you end up testing "method X called method Y" instead of observable behavior. Then any refactoring breaks the tests even without a bug. The practical rule is to double what is slow, unstable or has side effects (network, database, time, email) and let the business logic run for real.
Conclusion
A dummy fills, a stub answers, a fake implements lightly, a spy records and a mock demands. Knowing which one to use isn't trivia — it's what keeps your unit tests fast, isolated and focused on what really matters: the behavior of the code, not the details of whom it calls.