Configurando o Controlador Sob Teste em Modo Standalone
Ao concluirmos esta aula, nós:
- Compreenderemos por que devemos configurar o Sistema Sob Teste utilizando a configuração standalone (
standaloneSetup) quando escrevemos testes unitários de controladores. - Saberemos como configurar o controlador sob teste passo a passo usando
MockMvcBuilders.standaloneSetup(...). - Saberemos como evitar código duplicado de configuração na suíte de testes criando uma classe Object Mother (
WebTestConfig).
[!NOTE] Esta aula assume que:
Por Que Devemos Usar a Configuração Standalone em Testes Unitários?
Quando configuramos o Sistema Sob Teste para testar um controlador Spring MVC, precisamos criar um novo objeto MockMvc que atua como ponto de entrada para os nossos testes. Podemos criar esse objeto usando uma de duas abordagens:
- Configuração Standalone (
MockMvcBuilders.standaloneSetup): Instanciamos o controlador manualmente e configuramos apenas a infraestrutura essencial do Spring MVC em memória, sem subir nenhumApplicationContextdo Spring. - Configuração Baseada no Contexto Web (
MockMvcBuilders.webAppContextSetup/@WebMvcTest): Carrega umWebApplicationContextdo Spring gerenciado peloSpringExtension(@ExtendWith(SpringExtension::class)).
Quando estamos escrevendo testes unitários, devemos utilizar a configuração standalone pelos três motivos a seguir:
1. Liberdade Total para Escrever Testes Unitários Aninhados Rápidos (@Nested)
No JUnit 4 clássico, usar a configuração baseada em contexto web exigia anotar a classe com @RunWith(SpringJUnit4ClassRunner.class), o que impedia o uso simultâneo do @RunWith(HierarchicalContextRunner.class) para testes aninhados!
Mesmo no JUnit Jupiter 6 (onde @Nested é nativo), carregar um ApplicationContext do Spring em classes de teste com dezenas de subcontextos @Nested adiciona sobrecarga de inicialização de container IoC, cache de contexto e reflexão. Com standaloneSetup, não há container Spring: cada teste roda na velocidade pura do JUnit + MockK (em 1 a 2 milissegundos)!
2. É Muito Mais Fácil Selecionar Exatamente os Componentes Incluídos no SUT
Se utilizarmos uma configuração baseada em contexto de aplicação (ApplicationContext), teremos que levar nossos testes unitários em consideração ao estruturar as classes @Configuration da aplicação ou manter classes de configuração paralelas para os testes.
Com a configuração standalone, nós instanciamos apenas o controlador testado e anexamos explicitamente no StandaloneMockMvcBuilder somente os componentes de infraestrutura que aquele controlador realmente utiliza.
3. Podemos Criar os Dublês de Teste Diretamente na Classe de Teste que Precisa Deles
Se usarmos a configuração baseada em contexto web do Spring, precisamos registrar nossos dublês de teste como beans dentro do ApplicationContext do Spring (por exemplo, via @MockitoBean / @MockkBean ou classes @Configuration de teste).
Quando temos vários dublês ou múltiplos cenários aninhados, gerenciar beans mockados no container do Spring exige resetar os dublês antes de cada método de teste, pois todos os métodos compartilham a mesma instância do bean no contexto! Isso adiciona complexidade desnecessária e acoplamento entre casos de teste.
Já com standaloneSetup, criamos nossos dublês com mockk() / stub() diretamente no @BeforeEach da nossa classe de teste e os passamos para o construtor do controlador (HelloController(service)), exatamente como fazemos em qualquer outro teste unitário!
Vamos descobrir como configurar o Sistema Sob Teste passo a passo usando a configuração standalone.
Utilizando a Configuração Standalone Passo a Passo
Para configurar o Sistema Sob Teste usando a configuração standalone, seguimos estes três passos:
Passo 1: Adicionar o campo mockMvc à classe de teste
Primeiro, adicionamos uma propriedade private lateinit var mockMvc: MockMvc à nossa classe de teste HelloControllerTest:
|
|
Passo 2: Criar o método de setup (@BeforeEach)
Segundo, adicionamos um método de configuração anotado com @BeforeEach, que será executado antes de cada método de teste:
|
|
Passo 3: Instanciar o MockMvc com MockMvcBuilders.standaloneSetup()
Terceiro, implementamos nosso método de setup criando um novo objeto MockMvc. Fazemos isso invocando o método estático standaloneSetup() da classe MockMvcBuilders, passando a instância do controlador testado (HelloController()) como parâmetro e chamando .build() no StandaloneMockMvcBuilder retornado.
Após criarmos o objeto MockMvc, o código-fonte da nossa classe de teste fica assim:
|
|
Além do controlador em si, também podemos configurar outros componentes de infraestrutura do Sistema Sob Teste utilizando a API fluente da classe StandaloneMockMvcBuilder (como um ViewResolver, um HandlerExceptionResolver, um LocaleResolver ou conversores JSON HttpMessageConverter).
Contudo, se precisarmos configurar esses componentes adicionais em várias classes de teste de controladores, não devemos recriá-los manualmente dentro de cada classe de teste, pois isso geraria código duplicado difícil de manter. Vamos ver como resolver esse problema!
Configurando Componentes Adicionais sem Duplicação (WebTestConfig)
Quando precisamos configurar componentes adicionais de infraestrutura do Spring MVC nos nossos testes unitários, normalmente temos três objetivos:
- Queremos incluir os componentes adicionais necessários no Sistema Sob Teste.
- Queremos evitar escrever código duplicado entre diferentes classes de teste.
- Queremos manter nossas classes de teste o mais fáceis de ler possível.
Alcançamos esses três objetivos criando uma classe Object Mother chamada WebTestConfig (em Kotlin, um object WebTestConfig) e concentrando a criação e configuração desses componentes auxiliares em métodos fábrica dentro dela:
|
|
Depois de adicionarmos os métodos fábrica necessários em WebTestConfig, podemos incluí-los no Sistema Sob Teste invocando os métodos de configuração do StandaloneMockMvcBuilder (que veremos em detalhes na próxima aula!).
Prós e Contras Dessa Abordagem
Essa abordagem com standaloneSetup + WebTestConfig oferece três grandes benefícios:
- Não escrevemos código duplicado nas classes de teste, pois nossos componentes customizados de infraestrutura web são configurados em um único lugar (
WebTestConfig). - Vemos imediatamente no método
@BeforeEachde cada teste quais componentes fazem parte da configuração do Sistema Sob Teste. - Documentamos o propósito dos componentes através dos próprios nomes dos métodos fábrica em
WebTestConfig(comoexceptionResolver(),fixedLocaleResolver(),objectMapperHttpMessageConverter()).
A única desvantagem dessa abordagem é que precisamos replicar em WebTestConfig a configuração desses componentes específicos que usamos na configuração de produção da nossa aplicação. Entretanto, os benefícios de velocidade, isolamento total e clareza nos testes unitários superam de longe essa pequena desvantagem!
Resumo
Nesta aula, aprendemos três pontos fundamentais:
- A configuração standalone (
standaloneSetup) nos dá a possibilidade de escrever testes unitários de controladores extremamente rápidos, isolados e organizados em hierarquias@Nestedfáceis de ler, escrever e manter. - Configuramos o Sistema Sob Teste utilizando as classes
MockMvcBuilderseStandaloneMockMvcBuilder. - Criar nossos componentes adicionais de infraestrutura do Spring MVC em uma classe Object Mother (
WebTestConfig) elimina duplicação e mantém os testes limpos.