Boas Práticas: Sincronizando Constantes e SQL
Ao concluirmos esta aula, nós:
- Compreenderemos por que espalhar constantes de dados de teste diretamente dentro de várias classes de teste prejudica a manutenibilidade da suíte.
- Saberemos como declarar constantes de forma centralizada e limpa usando
objects aninhados em Kotlin, criando uma verdadeira Linguagem Específica de Domínio (DSL) para os dados do nosso banco de testes.
Por Que Devemos Nos Importar com Isso?
Nas aulas anteriores, aprendemos como inserir dados conhecidos no banco de dados (usando datasets XML ou scripts @Sql) e como escrever asserções sobre o estado do banco. Contudo, para escrevermos testes de integração realmente limpos, precisamos conectar mais uma ponta:
Como utilizamos os mesmos dados de teste (IDs, títulos, e-mails, nomes) tanto nos nossos datasets SQL/XML quanto no nosso código Kotlin das classes de teste?
Em outras palavras, nossos métodos de teste precisam passar parâmetros de busca (como repository.findById(1L) ou mockMvc.perform(get("/api/task/1"))) e verificar os objetos retornados comparando-os com os valores que foram inseridos pelos datasets.
Vamos descobrir por que a solução mais óbvia quase sempre se transforma em um problema de manutenção!
O Problema de Declarar Constantes Dentro das Classes de Teste
A resposta mais imediata é declarar constantes privadas no topo da própria classe de teste (TaskControllerTest ou TaskRepositoryTest):
|
|
Essa abordagem funciona bem apenas se TaskControllerTest for a única classe de teste de todo o projeto que utiliza esses dados. Porém, como vimos na aula sobre boas práticas de inserção de dados, nós reutilizamos os mesmos datasets (persons.xml / insert-persons.sql e tasks.xml / insert-tasks.sql) em dezenas de classes de teste de integração (testes de repositório, testes de serviço, testes de controladores web e testes de API REST)!
Se duplicarmos essas constantes dentro de cada classe de teste que utiliza o dataset, criaremos uma suíte de testes frágil e difícil de manter. Sempre que precisarmos alterar um dado no nosso dataset, teremos que lembrar de:
- Modificar o arquivo de dataset (XML ou SQL).
- Procurar e atualizar manualmente as constantes espalhadas por diversas classes de teste que referenciam aquele registro.
É evidente que não devemos duplicar constantes entre várias classes de teste. Vamos resolver esse problema de forma elegante usando Objetos de Constantes (Constant Objects) em Kotlin!
Objetos de Constantes ao Resgate (Criando uma DSL de Dados em Kotlin)
Um objeto de constantes é uma estrutura centralizada que declara os valores presentes nos nossos datasets de banco de dados. Ao criarmos esses objetos em Kotlin, cumprimos dois objetivos:
- Princípio DRY (Don’t Repeat Yourself): Declaramos cada constante exatamente uma única vez no código de teste, tornando a manutenção trivial.
- Linguagem Específica de Domínio (DSL) para Dados de Teste: Damos nomes semânticos aos registros do banco de dados, tornando nossos testes de integração autoexplicativos!
Vamos construir um objeto de constantes que descreve as tarefas do nosso dataset em três passos simples:
Primeiro, criamos um object de nível superior com o nome no plural da entidade em questão (Tasks):
|
|
Convenção de Nomenclatura: Nomear os objetos de constantes usando o substantivo no plural da tabela/entidade (
Tasks,Persons,Tags) torna muito fácil encontrá-los com o auto-complete da IDE enquanto escrevemos testes de integração.
Segundo, adicionamos um object aninhado dentro de Tasks para representar uma tarefa específica do nosso dataset. O nome desse object aninhado deve refletir o significado de negócio daquela linha (por exemplo, WriteLesson):
|
|
Terceiro, declaramos dentro de WriteLesson todas as constantes que espelham exatamente os valores das colunas daquela linha no nosso dataset (tasks.xml ou insert-tasks.sql):
|
|
Veja como a leitura de um teste de integração fica cristalina quando utilizamos Tasks.WriteLesson:
|
|
Pronto! Criamos um catálogo centralizado de constantes para os nossos testes de integração. Se precisarmos alterar algum valor de teste no futuro, faremos a alteração apenas no dataset (tasks.xml / insert-tasks.sql) e no objeto Tasks.
Resumo
Esta aula nos ensinou três coisas fundamentais:
- Não devemos declarar constantes de dados de banco diretamente dentro de uma classe de teste quando múltiplas classes de teste compartilham o mesmo dataset.
- Objetos de constantes centralizados (
object Tasks,object Persons) tornam nossos testes de integração muito mais fáceis de manter, pois cada valor é declarado uma única vez no código Kotlin. - Ao escolhermos bons nomes para os objetos aninhados (
Tasks.WriteLesson.TITLE,Persons.JohnDoe.EMAIL), criamos uma Linguagem Específica de Domínio (DSL) que torna nossos testes de integração extremamente legíveis.