BIGtheme.net http://bigtheme.net/ecommerce/opencart OpenCart Templates
19/08/2017 - 9:03 AM

Introdução ao Mockito

Introdução

A prática de automação de testes traz uma série de benefícios ao ciclo de vida do desenvolvimento e teste de software, dentre os principais estão: a economia em tempo de execução dos testes, menos trabalho repetitivo e aumento na cobertura de testes perante o software.

Mike Cohn descreve em seu livro Succeeding with Agile a famosa pirâmide do teste, onde fica clara a destinação dos níveis de teste. O teste de unidade, nível do qual discutimos aqui neste post, testa a menor unidade de código de uma aplicação orientada a objetos, os métodos.

Para a criação dos testes de unidade é importante que alguns princípios de engenharia de software sejam seguidos na criação do código do software que será testado, entre eles, a alta coesão e o baixo acoplamento, que basicamente são, respectivamente, que cada classe tenha uma responsabilidade única (ex. a classe produto não deve gerenciar pedidos) e que uma classe não deve depender de outra para funcionar (ex. Para que o produto funcione a categoria deve estar funcionando).

Vemos então que a automação dos testes de unidade podem ajudar, mas que para isso o software também deve estar preparado para ser testado.

Veremos neste post uma introdução a automação de testes de unidade, além de conhecer um framework para criação de mocks inicialmente em Java chamado Mockito.

O que são testes de unidade?

O teste de unidade visa garantir que cada método faz o que ele deveria fazer, isso na perspectiva do código.

Vemos um exemplo abaixo:

public class Avaliacao
{
     public String avaliar(double notaprova, double notatrabalho)
     {
          if (notaprova >= 7)
               if (notatrabalho >= 7)
                    return “Aprovado”;
               else
                    return “Precisará repor a nota do trabalho”;
          else
               return “Terá de fazer DP”;
     }
}

Para este código terei de fazer ao menos 3 testes:

  1. notaprova = 7 e notatrabalho = 7 deve resultar em “Aprovado”
  2. notaprova = 7 e notatrabalho = 6.9 deve resultar em “Precisará repor a nota do trabalho”
  3. notaprova = 6.9 e notatrabalho = 0 deve resultar em “Terá de fazer DP”

Estes testes poderiam seriam feitos manualmente, mas teriamo um trabalho muito grande, uma vez que as aplicações geralmente possuem um nível de complexidade muito grande.

Automatizando testes de unidade

A automação dos testes de unidade ocorre através de utilização de frameworks, na maioria das vezes, de código aberto. Podemos destacar aqui algumas delas, JUnit para Java, NUnit para C#, PHPUnit para PHP, etc.

O JUnit é o framework que usaremos neste post, e pode ser baixado no link abaixo:

https://github.com/junit-team/junit/wiki/Download-and-Install

Para projetos Maven, basta adicionar a dependência abaixo:

<dependency>
     <groupId>junit</groupId>
     <artifactId>junit</artifactId>
     <version>4.12</version>
     <scope>test</scope>
</dependency>

A automação prevê que seja criados métodos de teste que consigam executar o trabalho manual, no exemplo acima, usando JUnit, teríamos algo como:

@Test
public void testeAlunoAprovado()
{
     // Arrange
     Avaliacao avaliacao = new Avaliacao();
     // Act
     String resultado = avaliacao.avaliar(7, 7);
     // Arrange
     Assert.assertEquals(“Aprovado”, resultado);
}
@Test
public void testeAlunoReporNotaTrabalho()
{
     // Arrange
     Avaliacao avaliacao = new Avaliacao();
     // Act
     String resultado = avaliacao.avaliar(7, 6.9);
     // Arrange
     Assert.assertEquals(“Precisará repor a nota do trabalho”, resultado);
}
@Test
public void testeAlunoComDP()
{
     // Arrange
     Avaliacao avaliacao = new Avaliacao();
     // Act
     String resultado = avaliacao.avaliar(6.9, 0);
     // Arrange
     Assert.assertEquals(“Terá de fazer DP”, resultado);
}

Vemos que ao fim de cada método existe uma asserção, executada através do objeto “Assert” que verifica se o primeiro parâmetro é igual ao segundo parâmetro.

Para mais métodos da classe Assert, acesse o link abaixo:

http://junit.org/apidocs/org/junit/Assert.html

Uma vez escritos, os testes poderão ser executados automaticamente. Quando o método “avaliar” sofrer alterações e estas fizerem com que ele deixe de funcionar, após a execução dos testes estes falharão, dando um feedback imediato ao desenvolvedor.

Este é um outro grande benefício que adquirimos quando escrevemos testes de unidade automatizados: feedback instantâneo sobre áreas que funcionavam e deixaram de funcionar.

O método “avaliar” não possui integração com outras classes, mas se sim, teríamos que lidar com isso utilizando Mocks, para simular o funcionamento das classes externas, com o objetivo de isolar os testes de unidade.

O que são Mocks?

Segundo Martin Fowler, Mocks são “objetos pré-programados com expectativas que formam uma especificação das respostas que espera-se receber”.

Para entendermos melhor, imagine que o método “avaliar” precisa fazer uma chamada à métodos da classe “Aluno”, que ainda não foi criada, são eles: getNotaProva() e getNotaAluno().

Para preservar o princípio da coesão, foi criada uma interface chamada “Estudante”:

public interface IEstudante
{
     public double getNotaProva();
     public double getNotaTrabalho();
}

A classe “Aluno”, quando for criada, terá que declarar os métodos contidos na interface “IEstudante”, e desta forma, retornar as notas da prova e do trabalho quando for requisitada, vemos um exemplo de como a classe “Aluno” poderia ser codificada:

public class Aluno implements IEstudante
{
     public double getNotaProva(){
          return 7.0;
     }
     public double getNotaTrabalho(){
          return 7.0;
     }
}

Vamos agora alterar o método “avaliar”, passando por parâmetro apenas o objeto contendo a instanciação do aluno, e utilizaremos os métodos “getNotaTrabalho()” e “getNotaProva()”, como vemos abaixo:

public class Avaliacao
{
     public String avaliar(IEstudante aluno)
     {
          if (aluno.getNotaProva() >= 7)
               if (aluno.getNotaTrabalho() >= 7)
                    return “Aprovado”;
               else
                    return “Precisará repor a nota do trabalho”;
          else
               return “Terá de fazer DP”;
     }
}

Uma vez que o método mudou, ao executar os testes de unidade automatizados, eles iriam falhar, uma vez que ao estamos passando um parâmetro apenas, ao invés de dois. Por isso, iremos alterar também os testes de unidade, veremos apenas o exemplo do método de teste “testeAlunoAprovado()”:

@Test
public void testeAlunoAprovado()
{
     // Arrange
     Avaliacao avaliacao = new Avaliacao();
     IEstudante aluno = new Aluno();
     // Act
     String resultado = avaliacao.avaliar(aluno);
     // Arrange
     Assert.assertEquals(“Aprovado”, resultado);
}

E então finalizamos as implementações, levando em consideração que a classe “Aluno” já foi criada e funciona como esperado.

A questão é que existe uma boa prática em testes de unidade que aconselha que cada método de teste valide apenas uma classe, sem que haja dependências externas, e neste caso, teríamos que abstrair a utilização da classe “Aluno”, substituindo-a por um Mock, que simularia o funcionamento da mesma.

Em Java, alguns frameworks bastante conhecidos são, em ordem alfabética: EasyMock, Mockito e PowerMock. Neste post focaremos apenas no Mockito.

Introduzindo o Mockito

Mockito é um framework de código aberto para criação de Mocks. Foi escrito para escrita em Java, mas possui algumas iniciativas para utilização em outras linguagens como Python, Flex, Javascript, Scala, Objective C, Pearl, PHP e MATLAB.

Sua utilização é bastante simplificada, e para começar a utilizar basta baixar o jar a partir do link abaixo:

https://code.google.com/p/mockito/downloads/list

Ou, para projetos Maven, adicionar a dependência abaixo:

<dependency>
     <groupId>org.mockito</groupId>
     <artifactId>mockito-core</artifactId> 
     <version>1.10.0</version>
     <scope>test</scope>
</dependency>

Exemplo de utilização do Mockito

1) Utilizaremos o NetBeans como IDE de desenvolvimento. Desta forma, o primeiro passo é criar um novo projeto Java, clicando em:

Arquivo > Novo projeto > Java > Aplicação Java

2) Dê um nome ao projeto, neste tutorial, o chamaremos de “Escola”:

3) Clique com o botão direito no nome do projeto e selecione Novo > Outros:

4) Escolha a opção Testes de unidades > Testes JUnit:

5) Informe “Testes” como sendo o nome da classe de testes que será criada, informe o pacote “br.com.qualister.testes” como sendo o pacote onde ficará a classe, desmarque os checkboxes e pressione “Finalizar”:

6) Crie o diretório “lib” dentro do diretório “Escola” contido no diretório “NetbeansProjects”. Baixe o jar referente à versão 1.9.5 do Mockito-All e salve-o no diretório “lib”;

7) Baixe o jar referente à versão 4.12 do JUnit e salve-o no diretório lib do projeto Escola;

8) Clique com o botão direito no elemento “Bibliotecas” e selecione “Adicionar Jar”:

9) Escolha o jar do Mockito e do JUnit que foi salvo no diretório “lib” do projeto “Escola”;

10) Crie uma nova “Interface Java” no “Pacote de Códigos-fonte” do projeto:

11) Chame-a de “IEstudante” e use o pacote “br.com.qualister”:

12) Adicione os métodos getNotaProva() e getNotaTrabalho() a esta interface:

package br.com.qualister;

public interface IEstudante {
    public double getNotaProva();
    public double getNotaTrabalho();
}

13) Crie a classe “Avaliacao” e adicione o método “avaliar”, dentro do pacote “br.com.qualister”, segundo o exemplo abaixo:

package br.com.qualister;

public class Avaliacao
{
     public String avaliar(IEstudante aluno)
     {
          if (aluno.getNotaProva() >= 7)
               if (aluno.getNotaTrabalho() >= 7)
                    return "Aprovado";
               else
                    return "Precisará repor a nota do trabalho";
          else
               return "Terá de fazer DP";
     }
}

14) Abra a classe “Teste” contida dentro do pacote de testes “br.com.qualister.testes” e adicione as importações à classe “Avaliacao” e à interface “IEstudante”:

import br.com.qualister.Avaliacao;
import br.com.qualister.IEstudante;

15) Adicione as importações ao JUnit e ao Mockito:

import org.junit.Assert;
import org.junit.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

16) Agora, criaremos o primeiro método de teste, para isso declare o método “testAlunoAprovado()” utilizando a anotação “@Test”, conforme abaixo:

@Test
public void testeAlunoAprovado()
{
     
}

17) Vamos agora adicionar instanciar a variável avaliação:

// Arrange
Avaliacao avaliacao = new Avaliacao();

18) Precisamos agora instanciar a Interface “IEstudante”, e teremos que fazer um Mock desta interface, desta forma evitaremos a dependência da classe Aluno, o primeiro passo é instanciar utilizando o método “mock()” do Mockito:

IEstudante aluno = mock(IEstudante.class);

19) Agora iremos definir quais são as notas que serão retornadas quando os métodos contidos na interface “IEstudante” forem chamados, serão respectivamente 7.0 e 7.0:

when(aluno.getNotaProva()).thenReturn(7.0);
when(aluno.getNotaTrabalho()).thenReturn(7.0);

20) Agora terminaremos fazendo a chamada ao método “avaliar” e então fazendo a asserção que validará se o método realmente funciona como esperado:

// Act
String resultado = avaliacao.avaliar(aluno);

// Arrange
Assert.assertEquals("Aprovado", resultado);

21) Agora é só executar os testes, clicando sobre o método de testes com o botão direito e então escolhendo a opção “Testar”. Então veremos o resultado:

22) Crie os outros dois testes “testeAlunoReporNotaTrabalho” e “testeAlunoComDP” e valide que também funcionam corretamente, e pronto!

Vemos que agora nosso teste testa apenas a classe “Avaliacoes” sem depender da classe “Aluno” tudo graças ao Mock que criamos.

Estes testes serão executados todas as vezes que o código sofrer alterações, assim garantiremos que o código não deixará de funcionar após manutenções.

Baixe o projeto no meu GitHub:

https://github.com/juliodelimas/introducao-mockito

Outras possibilidades

Esta é a forma mais básica de trabalhar com o Mockito, nos próximos episódios veremos outras funcionalidades.

Conclusão

Vimos que utilizar testes de unidade pode fazer com que tenhamos uma série de benefícios, mas que em contrapartida, temos que nos dedicar mais à criação de código seguindo princípios da engenharia de software.

Discutimos sobre testes de unidade e algumas de suas particularidades, além da automação dos testes utilizando JUnit, um framework criado para auxiliar a desenvolver este tipo de teste.

Conhecemos também algumas das principais funcionalidades do framework Mockito, voltado a criação de mocks em código java.

Referências

TestPyramid: http://martinfowler.com/bliki/TestPyramid.html

Mock aren’t Stubs: http://martinfowler.com/articles/mocksArentStubs.html

Entendendo Coesão e Acoplamento: http://www.devmedia.com.br/entendendo-coesao-e-acoplamento/18538

Implementing an interface: https://docs.oracle.com/javase/tutorial/java/IandI/usinginterface.html

Mockito for Other languages: https://code.google.com/p/mockito/wiki/MockitoForOtherLanguages

 

Autor: Júlio de Lima (julio.lima@qualister.com.br)
Fonte:http://www.qualister.com.br/blog/criando-mocks-com-mockito-parte-1-introducao

Sobre Júlio de Lima

Júlio de Lima
Especialista em teste de software com ênfase em automação de testes de software, possui formação em Tecnologia da Informação e certificações internacionais (CTFL e CTAL-TM pelo ISTQB) e nacional (CBTS pela ALATS) relacionada a testes de software além de ser certificado na ferramenta SoapUI Pro pela SmartBear Software. Atualmente é consultor de automação de testes e instrutor na Qualister. É professor do curso de pós-graduação em Qualidade de Software na Uniasselvi (Blumenau, SC). Trabalhou, entre outros, em projetos de teste em Cloud Computing na UOL, em ferramentas de negociação do mercado de ações (MegaBolsa e E-PUMA) na BM&FBovespa, em projetos de telecomunicações na Vivo via Aitec do Brasil e em softwares de gestão pública no Assessor Público. É grande entusiasta da disciplina de testes de software e práticas ágeis.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *