Testando sua aplicação web Spring

Este blog deixou de ser mantido, mas o autor continua escrevendo aqui. Não deixe de assinar o novo feed!

Depois de apanhar feio, finalmente consegui criar um caso de teste no JUnit para validar o funcionamento de uma classe de serviço, cujo acesso à camada de persistência é gerenciada pelo Spring. Como é a primeira vez que eu estou criando uma aplicação web utilizando o Spring, nada mais normal do que esbarrar em algumas dificuldades.

Apesar de ter apanhado feito um cavalo, a solução é bem simples. Aqui mostro como eu fiz isso no NetBeans, porém a mesma coisa pode ser facilmente portada para outras IDEs com suporte à Ant.

Vamos ao assunto :D

O problema era o seguinte: Os arquivos de configuração do Spring são colocados dentro da pasta WEB-INF (ou em uma subpasta dentro desta). Então, bastava configurar o listener do Spring no web.xml para permitir que o contexto seja carregado no momento do deploy da aplicação:

  1.  
  2. <web-app>
  3.     <context-param>
  4.         <param-name>contextConfigLocation</param>
  5.         <param-value>/WEB-INF/spring/context-*.xml</param>
  6.     </context>
  7.  
  8.     …
  9.  
  10.     <listener>
  11.         <listener-class>
  12.             org.springframework.web.context.ContextLoaderListener
  13.         </listener-class>
  14.     </listener>
  15.    
  16.     …
  17.  

Porém, depois de codificar alguns DAOs, eu resolvi testá-los com o JUnit. Dei uma lida na documentação do Spring e vi que o Spring fornece algumas classes utilitárias para facilitar a criação de testes unitários de classes que fazem acesso a dados.

Para utilizar a classe fornecida pelo Spring, foi preciso adicionar o arquivo spring-mock.jar nas bibliotecas de teste. A classe abaixo mostra como utilizar uma classe do Spring que é própria para realização de testes que disparam alterações no banco de dados, onde, depois de executar cada caso de teste, o Spring efetua um rollback na transação, mantendo a integridade do seu banco de dados:

  1.  
  2. package br.com.tritone.blog.model.service;
  3.  
  4. import br.com.tritone.blog.model.Category;
  5. import java.util.List;
  6. import org.springframework.test.AbstractTransactionalSpringContextTests;
  7.  
  8. /**
  9.  *
  10.  * @author Daniel Fernandes Martins
  11.  */
  12. public class CategoryHibernateServiceTest extends
  13.     AbstractTransactionalSpringContextTests {
  14.    
  15.     private CategoryService service;
  16.    
  17.    
  18.     public CategoryHibernateServiceTest(String testName) {
  19.         super(testName);
  20.     }
  21.    
  22.     public void setCategoryService(CategoryService service) {
  23.         this.service = service;
  24.     }
  25.    
  26.     protected String[] getConfigLocations() {
  27.         return new String[]{"WEB-INF/spring/context-*.xml"};
  28.     }
  29.    
  30.    
  31.     public void testGetCategory() {
  32.        
  33.         Long id = service.saveCategory(
  34.             new Category(null, "Sample category")
  35.         );
  36.         assertNotNull("The category wasn’t saved", id);
  37.        
  38.         Category category = service.getCategory(id);
  39.         assertNotNull("The category doesn’t exists", category.getId());
  40.     }
  41.    
  42.     public void testSaveCategory() {
  43.        
  44.         Long id = service.saveCategory(
  45.             new Category(null, "Sample category")
  46.         );
  47.         assertNotNull("The category wasn’t saved", id);
  48.     }
  49.    
  50.     public void testRemoveCategory() {
  51.        
  52.         Category cat = new Category(null, "Sample category");
  53.         Long id = service.saveCategory(cat);
  54.         service.removeCategory(cat);
  55.        
  56.         try {
  57.             service.getCategory(id);
  58.             this.fail("This category must not exists");
  59.         }
  60.         catch (Exception exc) {
  61.             // OK
  62.         }
  63.     }
  64.    
  65.     public void testGetAllCategories() {
  66.        
  67.         int numrec = service.getAllCategories().size();
  68.        
  69.         service.saveCategory(new Category(null, "Sample category"));
  70.         service.saveCategory(new Category(null, "Sample category"));
  71.         service.saveCategory(new Category(null, "Sample category"));
  72.         service.saveCategory(new Category(null, "Sample category"));
  73.         service.saveCategory(new Category(null, "Sample category"));
  74.        
  75.         List<category> list = service.getAllCategories();
  76.         assertEquals("Number of elements in the list isn’t expected",
  77.             list.size(), 5 + numrec);
  78.     }
  79. }
  80.  

O problema que eu estava encontrando ao fazer o teste funcionar é que os arquivos de contexto do Spring não eram encontrados dentro do classpath. Fiz inúmeras tentativas e gambiarras, nenhuma com sucesso. Mas, de repente, me deu uma luz e eu resolvi alterar o arquivo de build do NetBeans para que, depois de compilados os testes, ele copiasse os arquivos de contexto do Spring para o classpath do código de testes.

Para fazer isso temos de alterar dois arquivos. Primeiramente, abra o arquivo situado no diretório nbproject/project.properties. No final deste arquivo, adicione a seguinte linha:

  1.  
  2. web.src.dir=${web.docbase.dir}/WEB-INF
  3.  

Finalmente, abra o arquivo build.xml, situado no diretório raíz do projeto. Insira o seguinte código dentro da tag project:

  1.  
  2. <target name="-post-compile-test"
  3.     depends="copy-spring-context" />
  4.  
  5. <target name="copy-spring-context">
  6.     <copydir src="${web.src.dir}/spring"
  7.         casesensitive="true"
  8.         dest="${build.test.classes.dir}/WEB-INF/spring"
  9.     />
  10. </target>
  11.  

Ao executar o teste, o Spring injeta dentro do mesmo um objeto do tipo CategoryService via auto-wire (por tipo). Dessa forma, fica muito fácil criar testes que utilizem beans declarados no Spring. Basta declarar um método set que o Spring injeta uma instância de forma automática. Caso existam beans de tipos iguais declarados no contexto, basta que você troque o tipo de auto-wire ou retire os métodos set e recupere os objetos manualmente dentro dos seus testes, através do objeto this.applicationContext.

A cada dia que passa eu me surpreendo mais com o Spring. Êta framework porreta, esse!

Tags: , , , ,