Categorizando testes com JUnit

Neste post, vamos ver como categorizar seus testes com JUnit, utilizando o recurso de Categories. As JUnit Categories apareceram na API do JUnit na versão 4.8 (a versão atual é a 4.11), com o intuito de tornar possível organizar testes em grupos. Essa feature veio para suprir uma das desvantagens existentes do JUnit em relação ao TestNG, que já possui um recurso de agrupar testes desde quando foi criado.

Por que usar?

Quando trabalhamos com grandes suites de testes, é normal ter um subconjunto contendo os testes mais importantes, aqueles que podem nos dar um feedback mais rápido sobre a estabilidade do build. Esse subconjunto é um velho conhecido dos últimos posts, são os Smoke Tests.

Além de Smoke Tests, também faria sentido categorizar testes que só funcionam em um browser específico (por exemplo, o Internet Explorer, que é o pesadelo da automação de testes...), testes que necessitem alguma configuração específica da sua aplicação, testes que usem carga de banco de dados (com DBUnit ou algo similar), testes que estejam instáveis (devido a alguma falha intermitente ainda não investigada), testes que rodam muito rápido (ou muito devagar), etc..

Como usar?

Para usar as Categories, são necessários três passos:

  • Criar marcadores para as categorias
  • Categorizar métodos (ou classes) de teste
  • Configurar a suite para executar os testes
Criando marcadores para as categorias

No JUnit, para definir uma categoria basta criar uma classe ou interface com o nome correspondente. Como as categorias funcionam apenas como marcadores, é recomendável usar interfaces.

Para cada categoria, criamos uma interface. Exemplo:

public interface SmokeTests {}  
public interface TestesInternetExplorer {}  
public interface TestesComCargaDeBanco {}  
public interface TestesInstaveis {}  
Categorizando métodos (ou classes) de teste

Para aplicar uma categoria a um método ou classe de teste, basta usar a anotação @Category, passando como parâmetro uma ou mais categorias.

Categorizando um método de teste, com uma ou mais categorias:

public class UsuarioTest {

   @Category(SmokeTests.class)
   @Test
   public void incluirUsuarioComSucesso() {
      //Smoke test
   }

   @Category({SmokeTests.class, TestesComCargaDeBanco.class})
   @Test
   public void editarUsuarioComSucesso() {
         //Smoke test E roda carga de banco
      //Duas categorias associadas
   }

}

Categorizando uma classe de teste:

@Category(TestesInternetExplorer.class)
public class UsuarioTest {  
   @Test
   public void incluirUsuario() {}

   @Test
   public void incluirUsuarioSemSucesso() {}

   @Test
   public void excluirUsuario() {}
}

No exemplo acima, os três métodos de teste pertencerão à categoria definida na classe UsuarioTest.

Configurando a suite para executar os testes

No JUnit, uma suite é definida através de uma classe anotada com @RunWith(Suite.class) e com a anotação @SuiteClasses informando quais classes de teste pertencem à suite. Antes de ver um exemplo com Categories, vejamos primeiro como definir uma suite sem Categories:

@RunWith(Suite.class)
@SuiteClasses({
UsuarioTest.class,  
LoginTest.class,  
AlunoTest.class  
})
public class TestSuite {}  

Usando Categories, precisamos dizer para a suite qual categoria queremos rodar ou qual não queremos rodar. Além disso, devemos trocar o parâmetro da anotação @RunWith, informando Categories.class em vez de Suite.class. Podemos assumir que Categories são um tipo de Suite (segundo a wiki do JUnit no GitHub).

Para incluir ou excluir uma categoria, basta anotarmos a suite com @IncludeCategory ou @ExcludeCategory, passando a categoria como parâmetro. Exemplo:

@RunWith(Categories.class)
@IncludeCategory(SmokeTests.class)
@ExcludeCategory(TestesInstaveis.class)
@SuiteClasses({
UsuarioTest.class,  
LoginTest.class,  
AlunoTest.class  
})
public class SmokeTestsSuite() {}  

Rodando múltiplas categorias

Não é possível passar, como parâmetro das anotações @IncludeCategory / @ExcludeCategory, uma lista de categorias. Para que seu teste inclua mais de uma categoria, você pode usar:

  • Herança de categorias - criando uma nova categoria que estenda uma ou mais categorias. Por exemplo:
public interface IEOuComCargaDeBanco extends TestesInternetExplorer, TestesComCargaDeBanco {}  
  • Uma ferramenta de build, como o Maven - o maven-surefire-plugin (para testes de unidade) e o maven-failsafe-plugin (para testes de integração) permitem configurar uma lista de grupos a incluir ou excluir. Isso é feito através das tags groups e excludedGroups, exemplo:
<groups>SmokeTests, TestesComCargaDeBanco</groups>  
<excludedGroups>TestesInternetExplorer</excludedGroups>  

Rodando categorias de maneira mais complexa

Observe que, em ambas as formas que explicamos acima, estamos agrupando categorias seguindo uma expressão lógica do tipo OR (OU). No exemplo acima, o Maven rodaria testes que contenham a categoria SmokeTests OU a categoria TestesComCargaDeBanco.

Para rodar categorias combinando expressões mais complexas (como rodar todos os testes que possuam, necessariamente, categorias "A" E "B"), não seria recomendado utilizar JUnit Categories. As Categories funcionam bem para casos mais simples, como os explicados ao longo do post. Entretanto, existem dois frameworks que fazem isso facilmente: o TestNG e o Cucumber-JVM.

Não vamos explorar esses frameworks mais a fundo no momento, mas vamos explicar abaixo como você poderia combinar suas categorias com mais flexibilidade em cada um deles:

  • TestNG - usando o recurso de grupos

O TestNG oferece grande flexibilidade para categorizar seus testes, através de grupos. Além disso, é muito simples configurar quais grupos deverão ou não ser executados em uma suite, permitindo inclusive utilizar scripts BeanShell. A documentação do TestNG é excelente e rica em exemplos, facilitando bastante o aprendizado.

  • Cucumber-JVM - usando tags

O Cucumber é um framework amplamente utilizado para se trabalhar com BDD. Mesmo que a implementação em Java não conte com todas as funcionalidades da original (em Ruby), a categorização de testes (que seriam cenários de features no Cucumber) é fantástica. Você pode organizar seus cenários com uma ou mais tags (@tag), e para rodar você pode combinar operadores lógicos (AND, OR, NOT) com facilidade. Esta apresentação do Alan Parkinson, além da documentação, são ótimas fontes para aprender sobre o Cucumber-JVM.


Qual framework devo usar?

Depende da sua necessidade e de como você trabalha atualmente. De acordo com o cenário em que você se encaixa, no mundo Java, possíveis recomendações seriam:

  • Você precisa categorizar testes de forma simples (como as categorias mostradas ao longo do post) e já utiliza o JUnit -> JUnit Categories

  • Você precisa de flexibilidade para categorizar testes...

    • ...e não pensa em trabalhar com BDD ou BDD não se aplica ao cenário da sua empresa -> TestNG
    • ...e pretende trabalhar ou já trabalha com BDD -> Cucumber-JVM

E você, já teve alguma experiência em categorizar testes? Compartilhem suas experiências e dúvidas nos comentários ou através dos meus contatos. Leiam também os links na seção Referências.

Até o próximo post! :)


Referências

https://github.com/junit-team/junit/wiki/Categories
http://java.dzone.com/articles/closer-look-junit-categories
http://www.lintips.com/?q=node/173
http://minds.coremedia.com/2012/08/30/experimenting-with-junit-categories/
https://weblogs.java.net/blog/johnsmart/archive/2010/04/25/grouping-tests-using-junit-categories-0
http://www.beanshell.org/


Sobre o autor: Stefan Teixeira trabalha como QA Engineer e, desde o final de 2014, tem se aventurado no mundo DevOps. É Bacharel em Ciência da Computação pela UFRJ e MBA em Garantia de Qualidade de Software pela Escola Politécnica da UFRJ. Entusiasta de Testes Automatizados (e de tudo que possa ser automatizado!), Agile Testing e da cultura DevOps.

Contatos: stefanfk@gmail.com | Twitter | LinkedIn


comments powered by Disqus