Não sou um especialista em TDD, na verdade escrevo muito menos testes do que seria realmente o ideal, mas depois da experiência que estou tendo com o SCRUM (ele é usado para gerenciar o projeto que estou participando no momento), comecei a me interessar mais sobre metodologias ágeis (até o ano passado quase todos os projetos em que eu trabalhava utilizavam aquele RUP Desengonçado/Waterfall bastante difundido aqui pelo brasil).
Andei dando uma olhada em TDD e comecei a estudar um pouco mais sobre testes (até com uma forcinha do Ruby On Rails), mas não sei se peguei bem os conceitos …
Pelo que eu entendi a idéia é mais ou menos assim:
Teste unitário = testar um método ou uma seqüencia de métodos de uma classe, sem a interferência de outras classes do sistema, caso a classe em testes tenha alguma dependência, serão fornecidos Mocks para substituir estas dependências, assim garantindo o comportamento esperado das dependências …
Teste de integração = testar um método ou uma seqüencia de métodos de uma classe, mas com as dependências reais para garantir que elas funcionam em conjunto …
Não sei se eu entendi corretamente, mas se for isto mesmo, segue um exemplo para quem estiver trabalhando com o Spring Framework …
Primeiro uma classe que será testada, esta classe extremamente complexa é a classe de negócios do exemplo, que contem um método “sum”, que recebe dois inteiros e deve retornar a soma destes números …
package sptestexample;
public class MyBusinessClass
{
public int sum(int i, int j)
{
return i+j;
}
}
Depois, eu criei um teste unitário simples, que instância manualmente a classe, se ela tivesse alguma dependência esta seria fornecida e os métodos a serem testados são chamados …
package sptestexample;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
public class MyBusinessTestExample
{
@Before
public void setUp() throws Exception
{
}
@Test
public void testCalculation() throws Exception
{
MyBusinessClass cls = new MyBusinessClass();
Assert.assertEquals(2, cls.sum(1,1));
int res = cls.sum(Integer.MAX_VALUE,Integer.MAX_VALUE);
Assert.assertEquals(res, Integer.MAX_VALUE+Integer.MAX_VALUE);
}
}
Como os testes unitários estão passando e me dizendo que a classe funciona, agora eu vou fazer um teste de integração, que neste exemplo vai consistir em buscar a classe do contexto do Spring Framework, que ja vai ter fornecido todas as dependências reais da classe, e vou repetir o teste anterior, mas tendo a classe integrada com o resto do sistema (dêem um desconto, é um exemplo simples :D)
package sptestexample;
import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
public class MyBusinessSpringTestExample extends AbstractDependencyInjectionSpringContextTests
{
protected MyBusinessClass myBusinessClass;
@Override
protected String getConfigPath()
{
return getClass().getSimpleName() + ".xml";
}
public MyBusinessSpringTestExample()
{
setPopulateProtectedVariables(true);
}
public void testCalculation()
{
assertEquals(2, myBusinessClass.sum(1,1));
int res = myBusinessClass.sum(Integer.MAX_VALUE,Integer.MAX_VALUE);
assertEquals(res, Integer.MAX_VALUE+Integer.MAX_VALUE);
}
}
Para que este teste funcione, eu precisei configurar a aplicação, pois o spring precisa de um XML para instanciar o contexto, neste caso o método getConfigPath esta retornando o mesmo nome da classe, por tanto o spring vai tentar inicializar o contexto com um arquivo de mesmo nome da classe, no mesmo package da classe, e o método setPopulateProtectedVariables que é chamado no construtor do teste, diz para o spring configurar as propriedades do TestCase usando as variáveis protected, ou seja, assim não preciso criar os setters das propriedades …
Como este é um exemplo complexo, o arquivo de configuração ficou como no exemplo abaixo:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" default-autowire="byName"> <bean id="myBusinessClass" class="sptestexample.MyBusinessClass"> </bean> </beans>
Beleza o meu teste de integração esta funcionando também …
A classe “AbstractDependencyInjectionSpringContextTests” como pode ser visto na imagem a baixo, é a classe base para todos os testes que precisam de um contexto do spring para funcionar (pelo que eu entendi, os testes de integração), ela permite injeção de dependências nos TestCases mas temos também algumas classes com recursos a mais para ajudar em situações mais especificas …

A classe “AbstractTransactionalDataSourceSpringContextTests” adiciona ainda alguns métodos auxiliares para os testes que vão mexer diretamente com JDBC …
A classe “AbstractAnnotationAwareTransactionalTests” adiciona ainda algumas anotações que podem ajudar bastante, como por exemplo:
@Transactional - que pode ser utilizada para forçar que um teste seja executado dentro de uma transação read-only
@NotTransactional - que faz com que um teste seja executado sem uma transação (o contrário do padrão para esta classe)
A classe “AbstractJpaTests” que tem diversos helpers para testar código que acessa a API JPA
E por último mas não menos importante, a classe “AbstractAspectjJpaTests” que faz a mesma coisa que a anterior, mas habilita o loadtime-weaving do aspectJ para que os testes rodem um pouco mais rápido …
Acho que era isto, a idéia deste post era verificar se eu entendi corretamente o que é cada tipo de teste, e ja deixar alguns exemplos para quem precisa testar aplicações que utilizam o Spring Framework
Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!
Tags: Java, produtividade, Spring Framework
Urubatan, esta diferença está correta em sua essência.
Em uma delas está o foco do teste está na unidade (neste caso um método de uma classe) enquanto em outro o foco é a integração desta unidade com o resto do sistema. Existem níveis de testes intermediarios (como o teste de “módulo”, “pacote” ou “dll”), que não passam de um teste unitário criado a Toddy na maioria das vezes.
Vc pode ter também, um teste de integração com cara de unitário, quando vc faz um codigo de teste intrusivo e testa a unidade sem os mocks. Neste caso esse codigo intrusivo pode ser chamado de test-service ou algo assim, quando vc quer testar uma funcionalidade a partir de um método *lá* dentro (em particular, isso facilita muito a automação de testes sem ter que depender de um browser, por exemplo, em um sistema web — testando a funcionalidade diretamente, vc testa o html de outra forma e cruza os resultados).
Acho que esse tipo de teste casa perfeitos com Aspectos, o que acha?
Reply to this commentnão entendi muito bem onde aspectos poderiam ajudar nestes testes …
Reply to this commentcriação de mocks? injeção de dependências?
Lembrando que nos testes de integração, as dependências externas ao sistema não necessariamente são outras classes, mas também banco de dados, servidores de aplicação, webservices disponibilizados pelo Google, etc…
Um link interessante sobre o assunto:
http://www.theserverside.com/tt/articles/article.tss?l=ManageTestDataSpringandDBunit
Abraços!
Reply to this commentOlá Urubatan..
Um cuidado que temos que ter ao usar o junit, é de não usá-lo apenas para automatizar testes e acabarmos não considerando bem alguns conceitos… o do teste unitário principalmente.
Indo um pouco na sua essência, o teste unitário (também chamado de caixa branca ou estrutural), tem um objetivo um pouco mais específico do que testar apenas entradas e saídas de métodos… Você deve sim fazer isto, mas tantas vezes quanto forem necessárias para testar todos os caminhos lógicos de processamento (fluxos e condicionais).
Muitas vezes, qdo se adota o uso de testes unitários, acaba se criando apenas um teste para cada método… considerando normalmente o fluxo normal de processamento…
Supondo que vc tenha um método com uma condicional, onde 99% das vezes o trecho de código dessa condicional não será executada, a probabilidade que os casos de teste não considerem essa situação é muito grande… É muito mais fácil o programador/projetista identificar isto e testar com junit, garantindo assim que não surgirão bugs “desconhecidos” depois de algum tempo em produção…
Para mim, o grande diferencial de qualidade que o teste unitário dá ao sistema é garantir que este trecho de código que vai ser executado em 1% das vezes esteja correto… Já que o resto acaba sendo testado nas outras fases do processo.
[]´s
Reply to this commentA questão da cobertura de testes foi bem lembrada pelo Diogo. Algumas ferramentas automatizam esta avaliação. Links em:
http://en.wikipedia.org/wiki/Code_coverage
[]s
Reply to this comment[...] um pouco e não deu tempo de passar mais com mais detalhes sobre a escrita de testes unitários (tem alguns exemplos aqui para quem quiser se adiantar), mas vou colocar os testes no exemplo que sera disponibilizado hoje a noite aqui no [...]
Reply to this commentMuito bom este post!
Reply to this commentEstou comecando a entender na pratica a (importante) diferenca entre testes unitarios e de integracao. Ter somente testes de integracao num ambiente Spring pode dar uma dor de cabeca danada, pois muitas vezes nao eh claro de onde veio a falha. Estou tendo esta experiencia agora, e esta mais dificil descobrir onde esta a falha do que reescrever os testes, de forma a ter unidade + integracao.
Valeu!
Srs,
Colocar o teste pra funcionar, então pra quem usa spring com anotations e quer colocar o junit, vai um pequeno post pra facilitar:
http://www.belisarioconsultoria.com.br/blog/?p=14
Reply to this comment