Mock 🧪
Imagine que você quer testar uma classe que envia e-mails, consulta um banco de dados ou consome uma API externa. Se essas dependências fizerem parte do teste, o resultado pode variar conforme o ambiente, a conexão ou o estado do sistema, tornando o teste lento, imprevisível e difícil de reproduzir. Objetos mock resolvem esse problema: eles simulam o comportamento das dependências para que você teste apenas o trecho de código que realmente importa.
Em Java, o Mockito é o framework mais utilizado para construir objetos mock. Também existem alternativas como EasyMock e JMock, mas o Mockito se destaca pela legibilidade e pela integração com o JUnit.
Um conceito central no Mockito é o stub: por meio de when(...).thenReturn(...) você combina com antecedência qual resposta a dependência simulada deve dar durante o teste, tornando o comportamento completamente previsível.
Anotações do Mockito
O Mockito oferece quatro anotações que aparecem com frequência em testes unitários: @Mock, @Spy, @InjectMocks e @Captor. Cada uma atende a um cenário diferente, e combiná-las bem é o que torna os testes expressivos e fáceis de manter.
@Mock
Pense em um dublê de cinema: ele substitui o ator real e executa exatamente o que o diretor planejou para aquela cena. A anotação @Mock faz o mesmo com dependências: cria uma instância simulada de uma classe ou interface e permite que você defina, via stub, o que cada chamada de método deve retornar ou lançar. Use-a sempre que sua classe depender de um recurso externo (repositório, API, gateway de pagamento etc.) e você quiser isolar esse recurso do teste.
// Estende o JUnit para suportar injeção de dependências com Mockito
@ExtendWith(MockitoExtension.class)
public class AppTest {
// Cria um objeto mock da interface DataBase
@Mock
DataBase base;
@Test
public void create() {
// Define o comportamento esperado do método createUser (stub)
when(base.createUser("Rodrigo")).thenReturn("Rodrigo");
assertEquals("Rodrigo", base.createUser("Rodrigo"));
}
@Test
public void delete() {
when(base.deleteUser(5L)).thenReturn(false);
assertEquals(false, base.deleteUser(5L));
}
@Test
public void deleteProblem() {
// Configura o mock para lançar exceção com argumento inválido
when(base.deleteUser(-1L)).thenThrow(new IllegalArgumentException());
// Verifica se a exceção lançada é a esperada
Assertions.assertThrows(IllegalArgumentException.class, () -> {
base.deleteUser(-1L);
});
}
}
Sem um stub configurado, o Mockito retorna valores padrão (null para objetos, 0 para números, false para booleanos), o que pode não representar seu caso de teste. Além disso, evite criar mocks em excesso: testes com muitas dependências simuladas tendem a ficar frágeis e difíceis de entender.
@Spy
Se @Mock substitui completamente a dependência por uma versão simulada, a anotação @Spy funciona de forma diferente: ela usa o objeto real, mas envolve esse objeto com uma camada de monitoramento. É como colocar uma câmera de segurança em uma sala: tudo continua funcionando normalmente, mas cada movimento fica registrado.
Com @Spy você consegue, ao mesmo tempo:
- executar o código real do objeto (sem simular nada);
- verificar quantas vezes um método foi chamado e com quais argumentos;
- sobrescrever o comportamento de métodos pontuais via stub, se necessário.
Exemplo 1: monitorando chamadas sem alterar o comportamento real
No exemplo abaixo, list é uma ArrayList real. O @Spy não muda nada no funcionamento dela: add de fato adiciona os itens e size de fato retorna o tamanho correto. O que muda é que o Mockito registra cada chamada, permitindo usar verify para confirmar que as interações aconteceram como esperado:
@ExtendWith(MockitoExtension.class)
public class MockitoSpyTest {
// list é uma ArrayList real — @Spy apenas a monitora
@Spy
private final List<String> list = new ArrayList<>();
@Test
public void shouldAddItemsToListSuccessfully() {
// Executa o código real: os itens são de fato adicionados à lista
list.add("one");
list.add("two");
// verify confirma que add() foi chamado 2 vezes com qualquer String
verify(list, times(2)).add(anyString());
// verify confirma que add() foi chamado com cada valor específico
verify(list).add("one");
verify(list).add("two");
// assertEquals confirma o estado real da lista — size() retorna 2
// porque os itens foram de fato adicionados (código real executado)
Assert.assertEquals(2, list.size());
}
}
Exemplo 2: sobrescrevendo um método pontual com stub
Às vezes o comportamento real de um método específico atrapalha o teste, por exemplo um método que acessa o banco de dados ou que retorna um valor difícil de controlar. Com @Spy é possível sobrescrever apenas esse método via when(...).thenReturn(...), mantendo o comportamento real dos demais.
No exemplo abaixo, add continua funcionando de verdade (os itens são adicionados), mas size é substituído por um stub que sempre retorna 100:
@ExtendWith(MockitoExtension.class)
public class MockitoSpyStubTest {
@Spy
private final List<String> list = new ArrayList<>();
@Test
public void shouldReturnDifferentSizeWhenStubbed() {
// Sobrescreve size() com um stub — apenas este método é simulado
when(list.size()).thenReturn(100);
// add() continua usando o código real — os itens são de fato inseridos
list.add("one");
list.add("two");
// verify confirma as interações com add(), que executou normalmente
verify(list, times(2)).add(anyString());
verify(list).add("one");
verify(list).add("two");
// size() retorna 100 (stub), não 2 (valor real)
// isso permite simular cenários sem depender do estado interno da lista
Assertions.assertEquals(100, list.size());
}
}
A diferença fundamental entre os dois exemplos é: no primeiro, tudo é real; no segundo, apenas size é simulado, enquanto o restante continua executando código real. Prefira @Spy quando o comportamento real do objeto é importante para o teste e você só precisa monitorar ou ajustar partes específicas. Se você se pegar substituindo muitos métodos via stub, considere usar @Mock diretamente, pois isso é um sinal de que o objeto real não contribui para o teste.
@InjectMocks
Ao escrever testes, montar manualmente um objeto que possui diversas dependências pode ser trabalhoso. A anotação @InjectMocks automatiza esse processo: ela cria uma instância da classe testada e injeta nela os mocks declarados no mesmo teste. Funciona como encaixar peças em um quebra-cabeça, onde o Mockito encontra o lugar certo para cada peça simulada.
No exemplo abaixo, a interface Network é uma dependência da classe Communication:
public interface Network {
public boolean send(String message);
}
public class Communication {
private Network network;
public boolean send(String message) {
boolean result = false;
try {
result = network.send(message);
} catch (Exception e) {
// TODO: handle exception
}
return result;
}
}
@ExtendWith(MockitoExtension.class)
public class MockitoInjectMocksTest {
// A interface Network será simulada
@Mock
Network network;
// O Mockito cria Communication e injeta o mock de Network automaticamente
@InjectMocks
Communication communication;
@Test
public void injectMocksTest() {
when(network.send("message")).thenReturn(true);
Assertions.assertEquals(true, communication.send("message"));
}
}
Lembre-se de que @InjectMocks depende dos mocks declarados com @Mock no mesmo arquivo de teste: não declare apenas @InjectMocks e espere que as dependências apareçam sozinhas. Valide sempre o comportamento da classe testada, não apenas os retornos das dependências simuladas.
@Captor
Às vezes o método que você quer testar não retorna o objeto de interesse: ele simplesmente o repassa para outra dependência. Nesses casos, é como querer verificar o conteúdo de um pacote depois de entregá-lo: você precisa interceptar o pacote antes do envio para conferir o que está dentro. A anotação @Captor, usada em conjunto com ArgumentCaptor, faz exatamente isso: captura o argumento passado para um método de uma dependência simulada para que você possa inspecioná-lo.
Para entender o problema que @Captor resolve, considere a classe abaixo. O método send recebe dados simples (destinatário, assunto, corpo e um sinalizador HTML), monta um objeto Email internamente e o entrega à plataforma. O objeto Email nunca é retornado; ele simplesmente some para dentro de platform.deliver():
public class EmailService {
private DeliveryPlatform platform;
public EmailService(DeliveryPlatform platform) {
this.platform = platform;
}
public void send(String to, String subject, String body, boolean html) {
Format format = Format.TEXT_ONLY;
if (html) {
format = Format.HTML;
}
// Email é construído internamente — o teste não tem acesso a ele
Email email = new Email(to, subject, body);
email.setFormat(format);
platform.deliver(email);
}
}
O que queremos testar é se o Email foi montado corretamente antes de ser entregue — em especial, se o formato foi definido como HTML quando o parâmetro html for true. Sem @Captor, não há como acessar esse objeto no teste. Com @Captor, o Mockito intercepta a chamada a deliver() e guarda o argumento para que possamos inspecioná-lo:
@ExtendWith(MockitoExtension.class)
public class EmailServiceUnitTest {
// Simula a plataforma de entrega — não queremos enviar e-mails de verdade
@Mock
DeliveryPlatform platform;
// Cria EmailService e injeta o mock de platform automaticamente
@InjectMocks
EmailService emailService;
// Declara um captor tipado: vai interceptar argumentos do tipo Email
@Captor
ArgumentCaptor<Email> emailCaptor;
@Test
public void whenDoesSupportHtml_expectHTMLEmailFormat() {
// Passo 1: executa o método que queremos testar
emailService.send("info@baeldung.com", "Assunto", "Corpo", true);
// Passo 2: usa verify para confirmar que deliver() foi chamado e,
// ao mesmo tempo, captura o Email que foi passado como argumento
verify(platform).deliver(emailCaptor.capture());
// Passo 3: recupera o objeto Email capturado
Email emailEnviado = emailCaptor.getValue();
// Passo 4: inspeciona o objeto — o formato deve ser HTML
assertEquals(Format.HTML, emailEnviado.getFormat());
}
}
O fluxo segue três responsabilidades bem separadas: @InjectMocks monta o objeto testado, verify + emailCaptor.capture() confirmam que a interação ocorreu e guardam o argumento, e assertEquals valida o conteúdo do objeto capturado. Remover qualquer uma dessas etapas enfraquece o teste: sem verify, o captor nunca é acionado; sem assertEquals, você confirma que deliver foi chamado mas não verifica se o e-mail estava correto.
verify vs. assert
Nos testes com Mockito aparecem dois tipos de verificação que têm propósitos distintos e complementares: assert e verify. Confundi-los é um erro comum que leva a testes que passam sem realmente validar o que deveriam.
O assert (JUnit) verifica o resultado: ele compara o valor retornado por um método com o valor esperado. A pergunta que responde é “o método devolveu o que eu esperava?”
O verify (Mockito) verifica o comportamento: ele confirma que um determinado método de um mock foi chamado, quantas vezes e com quais argumentos. A pergunta que responde é “a interação com a dependência ocorreu como planejado?”
@ExtendWith(MockitoExtension.class)
public class OrderServiceTest {
@Mock
PaymentGateway gateway;
@InjectMocks
OrderService orderService;
@Test
public void shouldChargeAndReturnConfirmation() {
when(gateway.charge(150.0)).thenReturn("TX-001");
String confirmation = orderService.placeOrder(150.0);
// assert: verifica o RESULTADO retornado pelo método testado
assertEquals("TX-001", confirmation);
// verify: verifica o COMPORTAMENTO — se o gateway foi chamado
// com o valor correto, exatamente uma vez
verify(gateway, times(1)).charge(150.0);
}
}
Use assert quando o método testado retorna um valor que você pode comparar diretamente. Use verify quando o método não retorna o dado de interesse, mas você precisa garantir que a dependência foi acionada corretamente, por exemplo, que um e-mail foi enviado, que um log foi registrado ou que um repositório foi chamado para persistir um objeto.
Evite substituir um pelo outro: um teste que só usa verify não checa o resultado produzido; um teste que só usa assert pode passar mesmo que a dependência nunca tenha sido chamada.
Código completo e repositório
Para obter o código completo dos exemplos apresentados:
git clone -b dev https://github.com/rodrigoprestesmachado/vvs
code vvs/exemplos/mockito/
Teste seus conhecimentos 🧠
Referências
- Mockito framework site. Disponível em: https://site.mockito.org.

CC BY 4.0 DEED