Testes unitários no spring boot para iniciantes: Por que testar antes de subir o código?
Testes unitários no spring boot para iniciantes e um tema que costuma gerar duvidas praticas em APIs Java modernas. Em projetos com Spring Boot, testes unitários não servem só para “aumentar cobertura”. Eles pegam regressões cedo, deixam refatorações menos arriscadas e ajudam a documentar o comportamento do código. Quando um service começa a crescer ou um controller precisa lidar com validações e respostas diferentes, um teste bem escrito evita aquela inspeção manual eterna no Postman. Se voce quiser comparar essa abordagem com outro cenario comum no ecossistema Spring, vale revisar JWT no Spring Security com Spring Boot: autenticação moderna passo a passo.
Na prática, o que mais importa no começo é separar o que você quer validar. Service deve testar regra de negócio. Controller deve testar entrada, saída e integração com a camada de serviço, sem subir banco, sem bater em fila e sem depender do resto da aplicação. Essa separação deixa os testes rápidos e fáceis de manter. Para complementar esse ponto com um exemplo proximo do dia a dia, consulte JWT Spring Boot Java: Guia Completo com Exemplo Prático.
Se você ainda está entendendo a base do ecossistema, vale revisar o que é o Spring Boot e para que serve?, porque isso ajuda a enxergar onde os testes encaixam na arquitetura. Esse detalhe conversa bem com o que eu mostrei em O que é o Spring Boot e para que serve?.
Testes unitários no spring boot para iniciantes: O kit básico: JUnit e Mockito
Para quem está começando, o combo mais comum é JUnit com Mockito. O JUnit organiza os testes, fornece anotações como Test, BeforeEach e Assertions, e o Mockito cria mocks para simular dependências. Isso permite testar uma classe isolada sem precisar instanciar o mundo inteiro ao redor dela. Se quiser aprofundar o assunto por outro angulo, leia tambem ResponseEntity no Spring Boot: Quando Usar e Exemplos Práticos.
Em aplicações Spring Boot, esse padrão aparece o tempo todo. Um service geralmente depende de um repository, outro service ou um client HTTP. Em vez de usar a dependência real, você injeta um mock e define o retorno esperado. Assim o teste fica previsível e a falha aponta exatamente para a regra que quebrou. Quando esse tipo de duvida aparece em projeto real, eu costumo voltar neste material: Controller vs RestController no Spring Boot: Qual usar em APIs REST?.
Outro ponto prático: teste unitário não é teste de integração. Se você está subindo contexto completo com banco em memória para validar uma regra simples, provavelmente está pagando mais caro do que precisa.
Exemplo de service com Mockito
Considere um service de pedido que depende de um repositório. A lógica é simples: se o produto existe e a quantidade é válida, o pedido é salvo. Esse é um bom candidato para teste unitário porque a regra está na classe, não na infraestrutura.
Classe de serviço
Um exemplo direto:
public class PedidoService {
private final PedidoRepository pedidoRepository;
public PedidoService(PedidoRepository pedidoRepository) {
this.pedidoRepository = pedidoRepository;
}
public Pedido criar(Pedido pedido) {
if (pedido.getQuantidade() <= 0) {
throw new IllegalArgumentException("Quantidade inválida");
}
return pedidoRepository.save(pedido);
}
}O teste abaixo valida o comportamento principal sem usar banco real:
@ExtendWith(MockitoExtension.class)
class PedidoServiceTest {
@Mock
private PedidoRepository pedidoRepository;
@InjectMocks
private PedidoService pedidoService;
@Test
void deveCriarPedidoQuandoQuantidadeForValida() {
Pedido pedido = new Pedido();
pedido.setQuantidade(2);
when(pedidoRepository.save(pedido)).thenReturn(pedido);
Pedido resultado = pedidoService.criar(pedido);
assertNotNull(resultado);
assertEquals(2, resultado.getQuantidade());
verify(pedidoRepository).save(pedido);
}
@Test
void deveLancarExcecaoQuandoQuantidadeForInvalida() {
Pedido pedido = new Pedido();
pedido.setQuantidade(0);
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> pedidoService.criar(pedido)
);
assertEquals("Quantidade inválida", exception.getMessage());
verifyNoInteractions(pedidoRepository);
}
}Repare no desenho do teste. O mock substitui o repository, o when define o retorno esperado e o verify confirma que a interação ocorreu. Quando a quantidade é inválida, nem faz sentido chamar o repositório; por isso o verifyNoInteractions deixa o teste mais honesto.
Testando controller com Spring Boot e Mockito
No controller, o foco muda. Você quer garantir que a requisição seja recebida, que o service seja chamado e que a resposta esteja correta. Se o objetivo for testar só a camada web, use @WebMvcTest com MockMvc. Isso evita subir o contexto inteiro e mantém o teste mais rápido.
Esse tipo de teste combina muito bem com controllers que retornam ResponseEntity no Spring Boot, porque você consegue validar status HTTP, corpo e headers com precisão. Também ajuda a separar melhor as responsabilidades discutidas em Controller vs RestController no Spring Boot.
Controller com dependência de service
@RestController
@RequestMapping("/pedidos")
public class PedidoController {
private final PedidoService pedidoService;
public PedidoController(PedidoService pedidoService) {
this.pedidoService = pedidoService;
}
@PostMapping
public ResponseEntity<Pedido> criar(@RequestBody Pedido pedido) {
Pedido criado = pedidoService.criar(pedido);
return ResponseEntity.status(HttpStatus.CREATED).body(criado);
}
}Teste com MockMvc
@WebMvcTest(PedidoController.class)
class PedidoControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private PedidoService pedidoService;
@Test
void deveRetornar201AoCriarPedido() throws Exception {
Pedido pedido = new Pedido();
pedido.setQuantidade(2);
Pedido criado = new Pedido();
criado.setQuantidade(2);
when(pedidoService.criar(any(Pedido.class))).thenReturn(criado);
mockMvc.perform(post("/pedidos")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"quantidade\":2}"))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.quantidade").value(2));
verify(pedidoService).criar(any(Pedido.class));
}
}O detalhe importante aqui é o uso de @MockBean. Em testes com Spring, ele injeta um mock dentro do contexto da aplicação para substituir o bean real. Isso é útil quando você quer subir apenas a fatia web e não precisa do restante da infraestrutura.
Quando usar @SpringBootTest e quando evitar
Quem está começando costuma achar que todo teste em Spring Boot precisa de @SpringBootTest. Na prática, essa anotação sobe muito mais coisa do que um teste unitário precisa. Ela é útil em cenários mais amplos, como integração entre camadas, configuração de beans ou validação de wiring da aplicação.
Se a sua intenção é testar uma regra de negócio isolada, prefira JUnit puro com Mockito. Se quer validar um endpoint completo com filtros, serialização e contexto web, use testes de controller. Se quer conferir integração real com banco, aí já entra outro nível de teste. Misturar tudo no mesmo tipo de teste deixa a suíte lenta e difícil de entender.
Em projetos com autenticação, por exemplo, testes de controller podem exigir mais cuidado quando há segurança no fluxo. Em cenários com JWT, vale separar bem o que pertence à autenticação e o que pertence ao endpoint. A base conceitual costuma ficar mais clara quando você entende primeiro JWT no Spring Security com Spring Boot e, em seguida, JWT Spring Boot Java: Guia Completo com Exemplo Prático.
Boas práticas que evitam testes fracos
Teste bom é teste que falha pelo motivo certo. Um erro comum é escrever testes muito acoplados à implementação, como validar cada chamada interna sem necessidade. Se amanhã você trocar a estrutura do service, o teste quebra mesmo com a regra ainda correta. Isso é ruído, não proteção.
Outra armadilha é usar dados demais no teste. Quanto mais objetos e cenários você monta sem foco, mais difícil fica entender o que realmente está sendo validado. Prefira um caso feliz e um caso de erro por vez. Nome de teste claro também ajuda bastante: ele deve dizer o comportamento esperado, não a linha de código coberta.
Também vale manter os testes rápidos. Se um teste de unitário começa a depender de contexto pesado, ele deixa de ser útil para a rotina do time. O ideal é rodar a suíte localmente várias vezes sem incômodo. Em time grande, esse detalhe faz diferença real na produtividade.
Conclusão
Testes unitários no Spring Boot ficam muito mais fáceis quando você entende a divisão entre service e controller. Service concentra regra de negócio e combina bem com JUnit e Mockito puro. Controller valida entrada, saída e integração com a camada de serviço, normalmente com MockMvc e @WebMvcTest. Esse recorte mantém os testes pequenos, rápidos e úteis.
Se o objetivo é escrever código mais seguro, comece pelo essencial: um serviço simples, um mock para a dependência e asserts que provem o comportamento esperado. A partir daí, a qualidade da suíte cresce junto com a aplicação, sem transformar teste em burocracia.
Testes unitários no spring boot para iniciantes: referencias externas
Para validar detalhes de implementacao e aprofundar a configuracao, vale consultar a documentacao oficial do Spring Security, o guia de claims no JWT.io e a documentacao do Spring Boot.
FAQ
Mockito substitui o JUnit?
Não. O JUnit organiza e executa os testes. O Mockito entra para simular dependências e isolar a classe que está sendo testada.
Preciso usar @SpringBootTest para testar controller?
Não necessariamente. Para testar só a camada web, @WebMvcTest costuma ser mais leve e direto.
Mock deve ser usado em todo teste?
Não. Use mocks quando a dependência é externa ao comportamento que você quer validar. Se o teste perde clareza por causa de mocks demais, talvez o recorte esteja errado. Em projeto real, testes unitários no spring boot para iniciantes costuma ser o ponto que mais exige clareza na configuracao.
Leitura complementar
Se voce quiser aprofundar esse assunto com um material mais atual, leia tambem Guia completo de testes no Spring Boot: evite falhas em produção.
Leitura complementar
Se voce quiser aprofundar esse assunto com um material mais atual, leia tambem Melhores equipamentos para programar sem gastar errado: evite.
Leitura complementar
Se voce quiser aprofundar esse assunto com um material mais atual, leia tambem Spring Boot Health Check Actuator Monitoramento sem falhas.
Leitura complementar
Se voce quiser aprofundar esse assunto com um material mais atual, leia tambem Testes integracao spring boot testcontainers: evite falhas em.