Facilitando seus testes de unidade no Java: um pouco de Mockito
Postado em 10. nov, 2011 por Raphael Lacerda em Agile, Java
Após um bom tempo de aulas ministradas, encontrei uma linha de aprendizagem que acho interessante para chegar até as consideradas boas práticas. A linha é formada pelos conceitos básicos de Orientação a Objetos, Testes, Injeção de Dependências, Programação orientada a interfaces e Mocks. Obviamente há uma interdependência dos tópicos. Há espaço aà para separação de responsabilidades com aspectos, além de design patterns.
Não é a toa que mocks sejam complicados para quem está começando, ele é totalmente obscuro (experiência própria), além de que, dependendo das unidades, é possÃvel testá-las sem mocks. Portanto, é mais interessante lidar com outros conceitos, como injeção de dependências, para só depois partir para Mocks.
O artigo não tem como objetivo explicar o conceito de Mocks, mas sim explicar a aplicabilidade do framework de testes Mockito nos seus projetos. Se você está começando agora, uma boa dica é procurar as edições da MundoJ (em quase toda edição há artigos relacionados a testes e consequentemente mocks) e depois ler o artigo do Martin Fowler.
Mesmo sem conhecer Mocks, lanço um desafio. Continue lendo o artigo e ao final sentirá quanto absorveu do conteúdo. Certamente terá dificuldades. Faça o bookmark do post, volte aos seus estudos, pratique e retorne depois. Ao reler o texto, perceberá o quanto evoluiu, resultado direto de seus estudos.
Então vamos ao código. Temos uma classe FuncionarioDAO e um método buscarFuncionario. No nosso exemplo, para buscar o Funcionario, o sistema deve se comunicar com um mainframe por meio de uma interface Transacao. Mas perceba aqui que poderia ser uma interface EJB comunicando-se remotamente ou até mesmo uma transação simples JDBC. Essa transação mainframe retorna uma String contendo as informações do usuário, separada por colunas. Então o que devemos fazer é uma lógica para montar um Funcionario a partir dessa resposta. Começando pelos testes, terÃamos o seguinte:
class FuncionarioDAOTest {
private FuncionarioDAO funcionarioDAO;
@Mock
private Transacao transacao;
@Before
public void init(){
MockitoAnnotations.initMocks(this);
funcionarioDAO = new FuncionarioDAO(transacao);
}
@Test
public void quandoUmUsuarioValidoForPesquisado(){
when(transacao.executar("12345")).thenReturn("RAPHAEL Â 12345 2045 ");
Funcionario funci = funcionarioDAO.buscarFuncionario("12345");
Assert.assertEquals("RAPHAEL", funci.getNome());
Assert.assertEquals(2045, funci.getSetor());
Assert.assertEquals("12345", funci.getMatricula());
verify(transacao, atMostOnce()).executar("12345");
}
@Test(expected=UsuarioInexistenteException.class)
public void quandoUmUsuarioInexistenteForPesquisado(){
when(transacao.executar("123")).thenThrow(new TransacaoOnlineException());
Funcionario funci = funcionarioDAO.buscarFuncionario("123");
}
}
O grande problema é a interface Transacao. Como estamos testando a unidade, não queremos que um objeto real se comunique com o mainframe: seria lento e difÃcil de testar o resultado, além de testar mais de uma unidade. Esse assunto já foi bastante discutido em artigos do blog da Caelum que falam de testes, sua influência no design, no acoplamento e na velocidade do seu projeto. Perceba o uso da annotation @Mock, ela facilita a criação de mocks, deixando o código mais limpo.
A primeira linha do primeiro teste (when(transacao.executar("12345")).thenReturn(" ... "))define como queremos que o mock se comporte durante a chamada do método executar. O Mockito leva uma grande vantagem aqui sobre os outros frameworks por ser extremamente refactoring friendly. Para isso, usamos o método when. Já na primeira linha do segundo teste podemos emular um erro através do thenThrow, ou seja, caso não haja nenhum funcionario, o método deverá lançar uma exceção. Com isso isolamos o teste apenas à unidade em questão, sem que código de outras unidades sejam executados.
Podemos ainda verificar se durante a execução de buscarFuncionario o método do nosso mock foi acionado. Para isso usamos o método verify. O segundo argumento pode receber alguns outros métodos do Mockito como never, atMostOnce, alLeastOnce, times().
Há algumas pessoas que acham desnecessário verificar se o método do objeto mocado foi realmente invocado. Inclusive a própria documentação do mockito comenta a respeito. Porém o uso do verify evita que alguém retire essa linha do seu código, algo bem difÃcil de acontecer, mas por experiência própria, podemos esquecer algum assert.
Feito os testes, partimos então para a lógica.
class FuncionarioDAO {
private Transacao transacao;
FuncionarioDAO(Transacao tx) {
this.transacao = tx;
}
public Funcionario buscarFuncionario(String matricula) {
try {
String resposta = transacao.executar(matricula);
return montarFuncionario(resposta);
}
catch(TransacaoOnlineException e){
throw new UsuarioInexistenteException(e);
}
}
private Funcionario montarFuncionario(String resposta) {
String nome = resposta.substring(0,10);
String matricula = resposta.substring(10,16);
String setor = resposta.substring(16,21);
return new Funcionario(nome.trim(),
matricula.trim(), Integer.parseInt(setor.trim()));
}
}
Agora, ao rodar os testes, você obterá a green bar!.
Vamos imaginar um cenário que você esteja lidando com um sistema com design mais pobre, com muito uso de métodos estáticos, e seu design ficasse assim:
class FuncionarioDAO {
public Funcionario buscarFuncionario(String matricula){
String resposta = TransacaoServiceLocator.executar(matricula);
return montarFuncionario(resposta);
}
}
Há algumas formas de testar esse código, ou seja, mocar o comportamento estático. A primeira é manipulação de bytecode (finamente explicado pelo meu amigo André Breves). Entretanto, não me arriscaria a fazer isso na mão, já existem frameworks para tal finalidade e um deles é o PowerMock, que se integra facilmente com Mockito e JUnit. É muito útil principalmente quando o framework que você está usando lhe impõe invocações estáticas a ele. Exemplificando, mesmo tendo D.I, inevitavelmente, uma hora ou outra utilizando o Seam 2.2, você invocará Component.getInstance().
Mas é sempre bom lembrar que fazer um design mais orientado a interfaces e desacoplado de implementações concretas lhe dará uma maior flexibilidade tanto na programação do sistema quanto na construção de testes. A discussão sobre utilização de métodos estáticos já está batida na comunidade, vários argumentos, dentre eles a programação mais estruturada e menos O.O.
A segunda seria encapsular o TransacaoServiceLocator em uma outra classe e mockar essa classe, sem ter de fazer malabarismos.
A terceira forma seria extrair a chamada para um método e na classe FuncionarioDAOTest, na criação de FuncionarioDAO, poderÃamos criar uma classe anônima e fazer o override do método buscarTransacao, retornando um mock.
public Funcionario buscarFuncionario(String matricula){
Transacao tx = Â buscarTransacao();
String resposta = tx.executar(matricula) ;
return montarFuncionario(resposta);
}
public Transacao buscarTransacao() {
return TransacaoServiceLocator.buscarTransacaoMainFrame();
}
Mas tudo isso é muito complicado, como o mockito te ajuda? Aà vêm os Spy Objects, a próposito, brilhantemente explicado aqui. Em suma, Spy Objects são objetos reais até que se prove o contrário. E provamos o contrário quando definimos algum comportamento para ele. Exemplificando:
class FuncionarioDAOTest{
private FuncionarioDAO funcionarioDAO;
@Mock
private Transacao transacao;
@Before
public void init(){
MockitoAnnotations.initMocks(this);
funcionarioDAO = spy(new FuncionarioDAO(transacao));
doReturn(transacao).when(funcionarioDAO).buscarTransacao();
}
Agora definimos que todos os métodos de FuncionarioDAO serão invocados normalmente, com exceção do método buscarTransacao, que teve o comportamento alterado. É importante reparar que eu usei doReturn ao invés do when.
Concluindo, o importante mesmo é saber lidar com as várias ferramentas que existem, tirando o maior proveito delas para construção de testes. Por exemplo, o meu framework não faz injeção de dependências por construtores, então já que eu não tenho construtores para receber os atributos, nos meus testes eu faço a injeção usando o Mirror, uma DSL que ajuda na manipulação da API reflection. Hoje mesmo já fui apresentando a uma outra ferramenta, fixture-factory. Indubitavelmente, conhecer o poder que cada ferramenta pode lhe oferecer e principalmente saber quando as utilizar são duas importantes tarefas de um arquiteto de software.
E quais ferramentas você utiliza para fazer os seus testes?
Créditos especiais ao companheiro de trabalho Taciano Tres,que me ajudou com ótimas referências e sempre me obriga a estar atualizado!
Raphael Lacerda
Mais sobre o autor
11 Respostas para “Facilitando seus testes de unidade no Java: um pouco de Mockito”
Trackbacks/Pingbacks
-
-
novembro 11, 2011
[...] http://blog.caelum.com.br/facilitando-seus-testes-de-unidade-no-java-um-pouco-de-mockito/ [...]
-
-
setembro 19, 2012
[...] ainda considerar um outro ponto relevante: o sistema possui testes automatizados? Unidade, Integração e principalmente Sistema? Caso negativo, seus custos podem ser bem mais altos do que [...]

ASSINE NOSSO RSS
Fábio Zoroastro
10. nov, 2011
Obrigado pelo post. Já trabalhei com o EasyMock, mas nunca com Mockito.
Numa próxima oportunidade, levarei em consideração avaliá-lo melhor.
Raphael Lacerda
10. nov, 2011
Opa.. Fabio
Tava com easymock e migrei pro mockito pra testar
Acabei achando mais interessante
Lucas Murata
12. nov, 2011
Muito bom o mockito.
É interessante olhar também o dirty-mockito que permite testes unitarios quando se usa JPA ou JSR303 com recursos de injeção.
Samuel
17. nov, 2011
Tetes unitários se tornam cada vez mais complexos a medida que vc tem um grafo maior de dependência em suas classes, sejam os famosos models quanto mesmo os daos, controllers.Normalmente essas classes seguem a idéia de composição e por isso o uso de interfaces, que as tornam uma especificação e logo bem flexÃveis.Isso é fato e portanto fazia-se necessidade do surgimento de ferramentas que facilitassem estes testes, o JMock é muito bom, mas o código limpo e suncito é uma das melhores coisas do mockito.O post está excelente, e a sugestão que deixo é um post futuro sobre Dehavior Drive Design e sobre NoSql especialmente os bancos orientados a doumentos,Abraços.
Raphael Lacerda
17. nov, 2011
Excelente observação Samuel! A ideia é exatamente essa!
Bom, sobre BDD, quem sabe um sobre JBehave…
vamos ver
vlw pela dica
André
23. nov, 2011
Muito bom o post.
Você pode também usar o MockitoJunitRunner para n precisar iniciar os mocks.
Existe também p @InjectMocks, caso seu objeto utilize deps injetadas por construtor.
Antonio Kantek
26. nov, 2011
Mockito eh legal. Mas o quente agora eh spock: http://code.google.com/p/spock/wiki/GettingStarted
juniorsatanas
06. dez, 2011
Muito Bom !
LeoLuz
21. fev, 2012
Ola Raphael,
Primeiramente parabéns pelo post!
Me ajudou bastante pois estou precisando implementar uma padronização de testes para a camada de serviços do projeto que estou trabalhando. Minha idéia era “mockar” os daos para testar de fato apenas a unidade. Como já tinha escutado muito bem sobre o mockito acabei caindo aqui no seu post.
Uma pequena diferença foi que usei a api de BDD do proprio mockito:
http://docs.mockito.googlecode.com/hg/latest/org/mockito/BDDMockito.html
Achei que dessa forma a expressividade do test fica melhor e bem parecida com a do Spock, mencionado acima.
Ao final criei um template do eclipse para ficar mais produtivo para nós seguindo esse exemplo:
http://www.rapaul.com/2009/08/09/bddmockito-eclipse/
Fica a dica.
Abraço.