Facilitando seus testes de unidade no Java: um pouco de Mockito

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 testessua influência no designno 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!

19 Comentários

  1. Fábio Zoroastro 10/11/2011 at 14:03 #

    Obrigado pelo post. Já trabalhei com o EasyMock, mas nunca com Mockito.

    Numa próxima oportunidade, levarei em consideração avaliá-lo melhor.

  2. Raphael Lacerda 10/11/2011 at 23:52 #

    Opa.. Fabio

    Tava com easymock e migrei pro mockito pra testar

    Acabei achando mais interessante
    🙂

  3. Lucas Murata 12/11/2011 at 11:44 #

    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.

  4. Samuel 17/11/2011 at 10:49 #

    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.

  5. Raphael Lacerda 17/11/2011 at 11:14 #

    Excelente observação Samuel! A ideia é exatamente essa!

    Bom, sobre BDD, quem sabe um sobre JBehave…

    vamos ver

    vlw pela dica

  6. André 23/11/2011 at 16:27 #

    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.

  7. Antonio Kantek 26/11/2011 at 10:55 #

    Mockito eh legal. Mas o quente agora eh spock: http://code.google.com/p/spock/wiki/GettingStarted

  8. juniorsatanas 06/12/2011 at 00:14 #

    Muito Bom !

  9. LeoLuz 21/02/2012 at 19:56 #

    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.

  10. Alexandre 01/09/2014 at 11:57 #

    Excelente artigo, estava escrevendo um post sob mocks e PowerMock e o achei. Se alguém necessitar trabalhar com o PowerMock, de uma olhada no post que escrevi. Nele eu uso o PowerMock para testar chamadas a funções de uma DLL carregada com JNA aonde eu substituo a DLL por um mock para permitir o teste.

    http://tekhton.blogspot.com.br/2014/08/fica-dica-testando-chamadas-dlls-usando.html

    Abração.

  11. Marcelo Soares 03/12/2014 at 11:01 #

    Olá Raphael.

    Primeiramente parabéns pelo seu post. Eu tenho um cenário ao qual gostaria de testar minha classe de teste onde eu realmente tenho uma conexão com o banco de dados. Para isso, teria um persistence-unit com as properties de conexão com o banco. Gostaria de saber como o Mockito resolveria este problema, já que o teste que tu apresentaste com o código é sem uma conexão.

    Obrigado por dividir teu conhecimento conosco e fico no aguardo de teu feedback.

    Marcelo

  12. Daniel 03/10/2015 at 17:03 #

    Parabéns pelo post.

    Gostaria de saber como fazer um mock de um objeto que na classe a ser testada é injetado?
    Quem puder ajudar….

    Abs

  13. Raphael 04/10/2015 at 23:32 #

    Simples, se a injeção for via constructor, problema resolvido, só passar o Mock no constructor

    Se a injeção for via atributos e vc quer fazer um teste de unidade, vai ter que usar reflection

    Recomendo o uso de uma DSL como o mirror.

    Se vc quer fazer testes de integração, dá uma olhada no Arquillian

Deixe uma resposta