Arquitetura de Execução de Testes E2E no Build
Ao concluir esta lição, você será capaz de:
- Saber como configurar diretórios de código-fonte e de recursos dedicados para os nossos testes End-to-End (E2E).
- Compreender como iniciar nossa aplicação web antes da execução dos testes E2E e encerrá-la automaticamente após o término dos testes.
- Executar seletivamente testes unitários, testes de integração ou testes End-to-End no ciclo de vida do build (com Maven Failsafe/Profiles e o paralelo na stack moderna com Spring Boot 4.1 e JUnit 6).
Esta lição assume que:
1. Os Requisitos do Nosso Build para Testes End-to-End
Os requisitos arquiteturais do nosso pipeline de build para suportar testes End-to-End são:
- Nossos testes End-to-End devem possuir um diretório de código-fonte separado:
src/end-to-end-test/kotlin(ousrc/endToEndTest/kotlin). - Nossos testes End-to-End devem possuir um diretório de recursos separado:
src/end-to-end-test/resources(ousrc/endToEndTest/resources). - Apenas os testes unitários devem ser executados por padrão no fluxo rápido de desenvolvimento.
- Devemos ser capazes de executar isoladamente apenas os testes unitários, apenas os testes de integração ou apenas os testes End-to-End.
- Nosso build (ou o suporte de testes do Spring Boot) deve iniciar nossa aplicação web antes da execução dos testes End-to-End e encerrá-la após a conclusão dos testes.
- Os nomes das nossas classes de teste End-to-End não precisam começar ou terminar com um sufixo arbitrário obrigatório (usamos categorias/tags semânticas como
@Tag("endToEndTest")/@EndToEndTest).
Vamos começar adicionando os novos diretórios de código-fonte e recursos ao nosso build.
2. Adicionando Diretórios de Código e Recursos ao Build
Como vimos na configuração dos testes de integração, em builds Maven adicionamos diretórios de código e recursos adicionais usando o Build Helper Maven Plugin (build-helper-maven-plugin). Como já declaramos esse plugin no nosso pom.xml, podemos registrar os diretórios dos testes End-to-End em dois passos:
Primeiro, criamos uma execução (add-end-to-end-test-sources) na fase generate-test-sources que adiciona o diretório de código-fonte dos nossos testes End-to-End (src/end-to-end-test/kotlin) ao build:
|
|
Segundo, criamos uma execução (add-end-to-end-test-resources) na fase generate-test-resources que adiciona o diretório de recursos dos nossos testes End-to-End (src/end-to-end-test/resources) ao build:
|
|
A seguir, veremos como configurar os perfis de execução do build para diferenciar testes de integração e testes End-to-End.
3. Configurando os Perfis de Execução e Tags do JUnit 6
Como executamos tanto os testes de integração quanto os testes End-to-End através do Maven Failsafe Plugin (nas fases integration-test e verify), precisamos ajustar a configuração dos nossos perfis em três passos:
Primeiro, renomeamos a propriedade skip.integration.tests para skip.failsafe.plugin. Fazemos isso porque o nome antigo não descreve mais com precisão o papel da propriedade, já que o plugin Failsafe agora executa tanto testes de integração quanto testes End-to-End:
|
|
Segundo, criamos um novo perfil chamado end-to-end-test. Esse perfil garante que apenas os testes End-to-End sejam executados quando ativado. Além disso, como o Maven Failsafe executa ambos os tipos de teste, criamos uma propriedade chamada junit.tag (equivalente moderna de junit.category no JUnit Platform 6) que define qual tag/meta-anotação (endToEndTest) será filtrada:
|
|
Terceiro, adicionamos a propriedade <junit.tag>integrationTest</junit.tag> também ao perfil integration-test. Isso garante que o Maven Failsafe execute exclusivamente as classes anotadas com @IntegrationTest (@Tag("integrationTest")) quando rodarmos os testes de integração:
|
|
Definindo a Meta-Anotação
@EndToEndTestem Kotlin:No JUnit Jupiter 6, criamos nossa meta-anotação semântica combinando
@Tag("endToEndTest"):
1 2 3 4 5 6 7 8package com.testwithspring.intermediate import org.junit.jupiter.api.Tag @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.RUNTIME) @Tag("endToEndTest") annotation class EndToEndTest
4. Configurando o Maven Failsafe Plugin
Ajustamos a configuração do maven-failsafe-plugin para cumprir dois requisitos:
- Garantir que o valor da propriedade
${junit.tag}especifique o grupo/tag dos métodos de teste invocados (<groups>${junit.tag}</groups>). - Garantir que o plugin não execute nenhum teste quando
${skip.failsafe.plugin}fortrue.
|
|
A seguir, veremos como iniciar e parar nossa aplicação web antes e depois dos testes End-to-End.
5. Iniciando e Parando a Aplicação Web
Antes de rodarmos nossos testes End-to-End, precisamos que a aplicação web esteja em execução ouvindo requisições HTTP reais em uma porta TCP, e que seja encerrada após o término dos testes.
Abordagem 1: Aplicações Spring Framework Tradicionais (WAR externo via Jetty Plugin)
Em aplicações Spring Framework sem Spring Boot empacotadas como .war, configuramos o jetty-maven-plugin dentro do perfil end-to-end-test em três passos:
- Declarar o plugin
jetty-maven-pluginna seção<build><plugins>do perfilend-to-end-test, apontando para o arquivo WAR gerado e configurandostopPortestopKey. - Adicionar uma seção
<executions>ao plugin do Jetty. - Criar duas execuções:
start-jetty: vinculada à fasepre-integration-test, executando o goalstartantes dos testes E2E iniciarem.stop-jetty: vinculada à fasepost-integration-test, executando o goalstopmesmo que algum teste E2E falhe (graças à separação entreintegration-testeverifydo Failsafe).
|
|
Duas observações importantes:
- Declaramos o
jetty-maven-pluginapenas dentro do perfilend-to-end-testporque não queremos subir o servidor HTTP completo quando executamos os testes de integração (-P integration-test), já que os testes de integração usamMockMvce subir o container web desnecessariamente deixaria a suíte mais lenta.- Abordagem 2 — Aplicações Spring Boot 4.1 (Recomendada): Se estivermos usando Spring Boot, não precisamos de plugins externos de container no Maven ou Gradle! O próprio suporte de testes do Spring Boot (
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)ouDEFINED_PORT) inicia o servidor embarcado (Tomcat/Jetty/Netty) automaticamente antes dos testes E2E e o encerra ao final. Veremos isso em detalhes no próximo tópico!
6. Executando Nossos Testes End-to-End
Podemos executar nossos testes End-to-End com o seguinte comando:
|
|
Ao executar esse comando no terminal, veremos na saída do console:
- O servidor web é iniciado na fase
pre-integration-test(ou pelo@SpringBootTestno início da suíte). - O Maven Failsafe Plugin executa exclusivamente os testes marcados com a tag
endToEndTest. - O servidor web é encerrado de forma limpa na fase
post-integration-testantes da verificação final da faseverify.
Vamos resumir o que aprendemos nesta lição.
Resumo
Esta lição nos ensinou quatro pontos essenciais:
- Podemos adicionar diretórios de código e recursos dedicados para testes End-to-End no Maven utilizando o
build-helper-maven-plugin(e no Gradle 9 de forma nativa comjvm-test-suite, como veremos na próxima aula). - Em aplicações Spring Framework clássicas sem Spring Boot, podemos iniciar e parar o servidor web nas fases
pre-integration-testepost-integration-testcom ojetty-maven-plugin. - Em aplicações Spring Boot 4.1, podemos iniciar e parar o servidor web embarcado diretamente através do suporte de testes do
@SpringBootTest. - Podemos executar de forma isolada tanto testes de integração quanto testes End-to-End utilizando o Maven Failsafe Plugin combinado com filtros de tags (
<groups>${junit.tag}</groups>).