Escolher o âmbito da evidência
Um teste unitário pode examinar uma regra com dependências controladas. Uma fatia WebMvcTest seleciona infraestrutura MVC e componentes relevantes, em vez de carregar indiscriminadamente toda a aplicação. SpringBootTest pode carregar um contexto mais amplo; o ambiente web escolhido determina se existe servidor real.
Escreve a conclusão pretendida antes de ler o resultado. Uma resposta JSON correta numa fatia com serviço simulado não demonstra commit, compatibilidade SQL ou acesso a uma API externa. Cada substituição reduz o âmbito da evidência, mesmo quando torna o teste rápido e útil.
Configurar uma fatia explícita
WebMvcTest pode fornecer MockMvc e apoio de segurança quando as dependências relevantes estão presentes. Importar a SecurityConfiguration que se pretende avaliar torna a política explícita. MockitoBean fornece ao controller um colaborador controlado; o comportamento configurado desse mock faz parte das premissas do resultado.
No Boot 4, usa os módulos e packages correspondentes à versão escolhida. O exemplo importa WebMvcTest de org.springframework.boot.webmvc.test.autoconfigure e MockitoBean de org.springframework.test.context.bean.override.mockito. Não transfiras imports antigos sem confirmar a API usada pelo projeto.
Uma identidade simulada não é login
WithMockUser coloca uma identidade no contexto de segurança do teste. O utilizador não precisa de existir na base e a sua palavra-passe não passa pelo mecanismo de autenticação real. O atributo authorities permite fornecer REPORT_WRITE literalmente; roles usa a convenção de prefixo ROLE_.
Isto é útil para comparar decisões de autorização com identidades controladas. Não demonstra a validação de uma palavra-passe, a assinatura de um JWT ou a disponibilidade do emissor. Mantém essas conclusões associadas a evidência própria, em vez de as deduzir de um controller autorizado com principal simulado.
Isolar o motivo de rejeição
Uma rejeição 403 pode resultar de autoridade insuficiente ou de CSRF, entre outras causas configuradas. Para comparar REPORT_WRITE com REPORT_READ num POST protegido, fornece token válido a ambos. Acrescenta uma comparação em que a autoridade é suficiente e o token está ausente ou é inválido.
A verificação deve também observar o efeito: numa rejeição anterior ao controller, o serviço simulado não deve ser chamado. Um caso positivo impede que uma política que bloqueia tudo pareça correta. Desativar filtros pode ser adequado a outro âmbito, mas deixa de sustentar conclusões sobre esses controlos.
Servidor real e dados isolados
SpringBootTest com MOCK não inicia normalmente um servidor embebido. RANDOM_PORT inicia um servidor numa porta atribuída e permite pedidos HTTP reais nesse ambiente. Isso acrescenta uma fronteira de execução: cliente e servidor trabalham em threads distintas e podem usar transações distintas.
Transactional no método do teste não reverte automaticamente os commits realizados pelo servidor. Se a comparação seguinte usa a mesma base, precisa de isolamento e limpeza explícitos. Mudar a porta não limpa registos. Distingue também este servidor local de um percurso real por gateway, TLS e balanceador externos.
Observar sincronização e confirmação
Num teste ORM, persist pode alterar o contexto sem enviar imediatamente todo o SQL. Se o teste termina com rollback antes da sincronização relevante, uma restrição da base pode nunca ser observada. Um flush explícito no ponto adequado permite verificar esse comportamento, sem equivaler a uma confirmação final da transação.
O estado da transação gerida pelo teste está associado à thread. Um timeout preemptivo que execute a ação noutra thread pode deixar efeitos confirmados fora do rollback esperado. Ao analisar um teste verde, pergunta que SQL foi observado, em que thread correu e que efeitos ficaram por limpar.
Interpretar a matriz do exemplo
O exemplo fornece o contrato ReportService, um controller que o invoca e três comparações com a política da aula anterior. O serviço é substituído em cada teste. permitsWriter combina REPORT_WRITE e csrf(); rejectsReader conserva csrf() e muda a autoridade; rejectsMissingCsrf mantém REPORT_WRITE e omite o token.
A distinção permite atribuir cada expectativa ao controlo relevante. A string de sucesso vem do mock e não certifica publicação nem persistência. A avaliação pede escolher interpretações, limites e próximos passos a partir do excerto; não exige executar JUnit, lançar um servidor ou escrever código.
// Separate files in package com.acme.reports.
// Uses SecurityConfiguration from the preceding lesson.
// Test dependencies: Boot 4.1.1 MVC test support, Spring Security test,
// JUnit Jupiter and Mockito. Read the snippet; execution is not assessed.
// ReportService.java
package com.acme.reports;
public interface ReportService { String publish(); }
// ReportController.java
package com.acme.reports;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ReportController {
private final ReportService service;
public ReportController(ReportService service) { this.service = service; }
@PostMapping(path = "/reports/publish", produces = "text/plain")
public String publish() { return service.publish(); }
}
// ReportControllerTests.java
package com.acme.reports;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.Mockito.*;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(ReportController.class)
@Import(SecurityConfiguration.class)
class ReportControllerTests {
@Autowired MockMvc mvc;
@MockitoBean ReportService service;
@Test @WithMockUser(authorities = "REPORT_WRITE")
void permitsWriter() throws Exception {
when(service.publish()).thenReturn("accepted-in-test");
mvc.perform(post("/reports/publish").with(csrf()))
.andExpect(status().isOk())
.andExpect(content().string("accepted-in-test"));
verify(service).publish();
}
@Test @WithMockUser(authorities = "REPORT_READ")
void rejectsReader() throws Exception {
mvc.perform(post("/reports/publish").with(csrf()))
.andExpect(status().isForbidden());
verifyNoInteractions(service);
}
@Test @WithMockUser(authorities = "REPORT_WRITE")
void rejectsMissingCsrf() throws Exception {
mvc.perform(post("/reports/publish"))
.andExpect(status().isForbidden());
verifyNoInteractions(service);
}
}
Compara os três métodos sem os executar. O primeiro combina autoridade suficiente e token válido; o segundo muda apenas a autoridade; o terceiro conserva a autoridade suficiente e omite o token. O retorno accepted-in-test é definido pelo mock e não prova que um relatório foi gravado.
Armadilhas comuns
Interpretar WithMockUser como login; desligar filtros para obter verde; atribuir qualquer 403 à autoridade; presumir rollback do servidor a partir de Transactional no cliente; confundir flush com commit.
Tópicos relacionados: Segurança e fronteiras de autorização · Persistência e fronteiras de transação · Empacotamento, entrega e aceitação operacional
Um resultado só sustenta o âmbito que foi exercitado. Explicita substituições, controlos ativos, threads e transações antes de concluir que o comportamento completo está validado.
Referência: Testing Spring Boot applications · Spring Boot 4.1.1 / Spring Framework 7.0.9 / Java 25