Populando o Banco de Dados com a Anotação @Sql em Testes E2E
Depois de ler esta aula, você entenderá:
- Por que devemos utilizar a anotação
@Sqlnos nossos testes End-to-End quando estamos escrevendo testes para uma aplicação Spring Boot. - Como podemos popular nosso banco de dados com dados de teste e limpá-lo após cada teste utilizando a anotação
@Sql.
Vamos começar descobrindo por que a anotação @Sql é a escolha ideal para gerenciar o estado do banco de dados em testes End-to-End com Spring Boot.
1. Por Que Usar a Anotação @Sql em Testes E2E?
Quando estamos escrevendo testes End-to-End para uma aplicação Spring Boot (iniciada pela própria suíte de testes com @SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT) ou RANDOM_PORT), devemos utilizar a anotação @Sql para inicializar nosso banco de dados pelos seguintes motivos:
- Não podemos usar a anotação
@Transactionalnos nossos testes End-to-End! Quando nossa aplicação web recebe uma requisição HTTP enviada pelo navegador (Selenium ou Playwright), o servidor HTTP embarcado processa essa requisição em uma thread do servidor separada da thread do JUnit e, portanto, em uma transação de banco de dados completamente diferente.- Se o teste inserisse dados dentro de uma transação aberta na thread do teste sem fazer commit, a thread do servidor HTTP não enxergaria esses dados!
- Além disso, as alterações feitas pela aplicação web durante a requisição HTTP são imediatamente comitadas no banco de dados assim que o controller retorna a resposta HTTP. Um rollback na thread do teste jamais desfaria o que o servidor HTTP comitou.
- Não precisamos adicionar bibliotecas extras de teste de banco (como DbUnit) apenas para popular tabelas com SQL puro: a anotação
@Sqljá faz parte nativa do módulospring-test. - Podemos inicializar o banco em um estado conhecido antes que cada método de teste seja executado e limpar o banco depois que cada método de teste terminar. Isso elimina completamente o acoplamento entre testes que discutimos na aula anterior!
Vamos ver agora como podemos criar os scripts SQL e configurar a anotação @Sql nas nossas classes de teste End-to-End.
2. Criando os Scripts SQL para os Testes End-to-End
Nossa aplicação de exemplo utiliza o Liquibase para criar o schema do banco de dados quando a aplicação é iniciada. Porém, como queremos ter isolamento total entre cada método de teste End-to-End, utilizaremos três scripts SQL localizados no diretório src/endToEndTest/resources/com/testwithspring/intermediate/:
users.sql: Insere as duas contas de usuário (John DoeeAnne Abbott) na tabelauser_accounts.tasks.sql: Insere as três tarefas de teste na tabelatasks, reinicia a sequencetasks_id_seqem4e insere a tagexamplenas tabelastagsetasks_tags.cleandb.sql: Remove todos os dados das tabelas do banco de dados após a execução de cada método de teste, respeitando a ordem das chaves estrangeiras (foreign keys).
Vamos analisar o conteúdo de cada um desses três arquivos:
1. Script users.sql
O script users.sql (src/endToEndTest/resources/com/testwithspring/intermediate/users.sql) insere os dois usuários conhecidos no banco de dados:
|
|
2. Script tasks.sql
O script tasks.sql (src/endToEndTest/resources/com/testwithspring/intermediate/tasks.sql) insere as três tarefas, atualiza o próximo valor da sequence tasks_id_seq para 4 e associa as tarefas à tag example:
|
|
3. Script cleandb.sql
O script cleandb.sql (src/endToEndTest/resources/com/testwithspring/intermediate/cleandb.sql) apaga os registros de todas as tabelas na ordem inversa das dependências de chave estrangeira (tasks_tags -> tags -> tasks -> user_accounts):
|
|
3. Configurando a Anotação @Sql na Classe de Teste
Agora podemos utilizar a anotação @Sql (que é uma anotação repetível — @Repeatable(SqlGroup::class)) na nossa classe de teste End-to-End para configurar duas fases distintas de execução:
- Antes de cada método de teste (
ExecutionPhase.BEFORE_TEST_METHOD— padrão): Executamos os scriptsusers.sqletasks.sqlpara popular o banco de dados em um estado conhecido. Como o scripttasks.sqlinsere linhas na tabelatasksque possuem chaves estrangeiras apontando parauser_accounts, devemos garantir queusers.sqlseja listado antes detasks.sql. - Depois de cada método de teste (
ExecutionPhase.AFTER_TEST_METHOD): Executamos o scriptcleandb.sqlpara esvaziar todas as tabelas do banco de dados, garantindo que o próximo teste encontre o banco completamente limpo.
Depois de anotarmos nossa classe de teste End-to-End com @Sql, o código-fonte fica assim:
|
|
Resumo
Nesta aula, aprendemos que:
- Não podemos utilizar
@Transactionalpara fazer rollback automático em testes End-to-End porque o servidor HTTP embarcado processa as requisições do navegador em outra thread e comita as transações imediatamente. - A anotação
@Sqldo Spring TestContext Framework nos permite executar scripts SQL comitados antes (BEFORE_TEST_METHOD) e depois (AFTER_TEST_METHOD) de cada método de teste. - Ao combinar
@Sql(BEFORE_TEST_METHOD)para popularusers.sqletasks.sqlcom@Sql(AFTER_TEST_METHOD)para executarcleandb.sql, garantimos isolamento completo entre todos os nossos testes End-to-End. - No próximo tópico, começaremos a escrever nossos testes End-to-End na prática: localizando elementos HTML, controlando o navegador, criando Page Objects e testando fluxos completos de leitura e submissão de formulários!