quinta-feira, 23 de abril de 2009

Testes Unitários da Camada de Persistência com Spring, Hibernate e JUnit.


O teste unitário é uma modalidade de teste voltada para a verificação de “pedaços de software” (ou unidades) que funcionam de maneira independente das demais partes de um sistema. Em softwares construídos em linguagens orientadas a objetos, essas unidades podem ser uma classe ou um método. Em linhas gerais, busca-se com testes unitários garantir que uma parte isolada de um sistema funcione corretamente de acordo com o contexto em que ela se encontra.

Atualmente, em projetos de software, muito se tem visto testes unitários para a camada de persistência com objetivo de assegurar que os objetos que representam o domínio de dados serão salvos, recuperados e excluídos corretamente. Em projetos em que há mudanças consideráveis no modelo de dados (devido a necessidade de adaptações ou mudança de requisitos) os testes unitários da camada de
persistência mostram-se como uma ferramenta poderosa na verificação dos impactos das mudanças na persistência dos dados. Afinal, nada melhor que após mudanças no projeto averiguar que partes isoladas ainda descrevem o comportamento esperado.

Nesta postagem será mostrada uma forma eficiente e prática de testar a camada de persistência de uma aplicação através do uso do spring-test (parte do spring destinada a testes unitários), juntamente com o JUnit. Por questões de praticidade será utilizado o hibernate para o mapeamento objeto-relacional do exemplo.

Para iniciar o exemplo, vamos definir a classe Pessoa que representará a entidade que será persistida no banco de dados.

import static javax.persistence.GenerationType.IDENTITY;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@SuppressWarnings("serial")
@Entity
@Table(name = "pessoa", catalog = "springtest")
public class Pessoa implements Serializable {

@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
private Integer id;
@Column(name = "nome")
private String nome;
@Column(name = "idade")
private Integer idade;
// getters e setters omitidos
}

Vamos também definir um DAO para pessoa cuja interface será:

public interface PessoaDao {
void save(Pessoa pessoa);
void delete(Pessoa pessoa);
Pessoa getById(Integer id);
}

Certamente para a classe que implementará a interface PessoaDao deverá ser definido um bean no contexto do spring.

Agora vamos montar os testes unitários da camada de persistência de pessoa utilizando o spring-test junto com o JUnit.


import static junit.framework.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;
import com.springtest.dao.PessoaDao;
import com.springtest.model.Pessoa;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"file:WebContent/WEB-INF/persistencia.xml" })
@Transactional
@TransactionConfiguration(defaultRollback = false)
public class PessoaTest {
@Autowired
private PessoaDao pessoaDao;

@Test
public void testSave() {
Pessoa pessoa = new Pessoa();
pessoa.setNome("Thiago Baesso Procaci");
pessoa.setIdade(22);
pessoaDao.save(pessoa);
assertNotNull(pessoa.getId());
}

@Test
public void testDelete(){
// implementacao do teste
}

@Test
public void testUpdate(){
// implementacao do teste
}
}
Através da classe de teste acima pode-se notar algumas facilidades que o spring-test e o JUnit oferecem através de anotações. Segue abaixo uma breve explicação de cada uma delas:
  • @RunWith(SpringJUnit4ClassRunner.class): anotação responsável por definir “quem” irá rodar os testes. No caso, aclasse SpringJUnit4ClassRunner já provê algumas facilidades para testes
    unitários que utilizam o spring;

  • @ContextConfiguration: carrega o contexto do spring (beans) para serem utilizados na classe de teste;

  • @Transactional / @TransactionConfiguration: definem cada método de teste unitário como uma transação;

  • @Autowired: Injeta os beans do spring para serem utilizados nos testes;

  • @Test: definem os métodos que serão os testes unitários.

Enfim, testes em geral são fundamentais para a assegurar a qualidade de qualquer sistema. Achei essa abordagem com o spring-test muito interessante e prática para a confecção de testes unitários.

Para quem quiser mais detalhes sobre o funcionamento do spring-test acesse:

http://static.springframework.org/spring/docs/2.5.x/reference/testing.html

Os fontes do exemplo estão disponíveis em:

http://sites.google.com/site/csscode/Home/Exemplo_Teste_Unitario.zip?attredirects=0

Abraço a todos!!!

Thiago Baesso Procaci

5 comentários:

Anônimo disse...

Já ouviu falar do HibernateMock

http://code.google.com/p/hibernatemock/

Thiago disse...

Fala aí mestre.
Já ouvi sim.
Para fazer testes unitários tenho usado junit + mockito + dbUnit.

Recomendo.

Wesley disse...

Olá! Gostei do seu post.
Voc jápassou por este problema:
Uso o Spring 3.05 c JUnit 4.7.
Ao tentar rodar o teste com com anotação @RunWith(SpringJUnit4ClassRunner.class)
acontence...
java.lang.NoSuchMethodError: org.springframework.core.annotation.AnnotationUtil s.findAnnotationDeclaringClass(Ljava/lang/Class;Ljava/lang/ClassLjava/lang/Class;
....
Fiz uma pesquisa mas não tive solução. Basicamente adicionei todos os jar' do spring e security com o jar do junit 4.7 e dep 4.7
Obrigado!

Thiago disse...

Fala Wesley!
Esse seu erro me parece problemas de compatibilidade entre a versão do junit e do spring.
Verifique uma versão do junit que seja compatível com o spring 3.05.

Esse post é um pouco antigo, e na época não tinha o spring 3.

Wesley disse...

Obrigado pela dica Thigo, um ótimo trabalho pra você.
Wesley -Gyn