Mostrando postagens com marcador Mocks. Mostrar todas as postagens
Mostrando postagens com marcador Mocks. Mostrar todas as postagens

quarta-feira, 6 de junho de 2012

Mocks com Mockito - Parte 3

Neste post vamos abordar o uso de Spies usando o Mockito.

Ao contrário de Mocks com os Spies é possível chamar os métodos reais da classe.

List<String> listaSpy = new ArrayList<String>();
listaSpy = Mockito.spy(listaSpy);

listaSpy.add("item1");
//Chama o método verdadeiro do objeto e retorna a String = "item1"
listaSpy.get(0); 
Mockito.doReturn("outra coisa").when(listaSpy).get(0);
//Não chama o método verdadeiro e retorna a String = "outra coisa"
listaSpy.get(0); 

Ao contrário dos Mocks um objeto Spy age como se fosse um Semi mock, em outras palavras,  quando não definimos um outro comportamento para o Spy, seu funcionamento é igual ao de um objeto normal. No entanto ao definirmos um outro comportamento (linha 8) ele se comporta como um Mock para o caso que definimos.

Ok e eu com isso?! Vamos ver um uso mais real de um spy.

O uso que eu tive ao usar o Spy foi para Mockar criação/instanciação de objetos.

Por exemplo, na classe Connection temos o seguinte método.

//declaração da classe Connection
public boolean connect() {
  try {
    socket = new Socket(hostAddress, port);
    printStream = new PrintStream(socket.getOutputStream());
    bufferedReader = new BufferedReader(
                       new InputStreamReader(socket.getInputStream()));
    return true;
  } catch (UnknownHostException e) {
    e.printStackTrace();
  } catch (IOException e) {
    e.printStackTrace();
  }

  return false;
}

public String readMessage() { 
//...
  return bufferedReader.readLine();
//...
}

Como faremos para testar esta classe e o método readLine(), se no cenário atual nós não podemos criar um mock de BufferedReader em nosso teste e passa-la para o objeto Connect, pois o BufferedReader é instanciado dentro da classe Connect!. :/

Spy ao seu resgate! Uma solução é usar Spies para simular isso... então nosso primeiro passo é criar métodos na classe Connect que criem os objetos que precisamos.

Ficando assim o código.

//declaração da classe Connection
public boolean connect() {
  //..
  socket = createSocket();
  printStream = createPrintStream();
  bufferedReader = createInputStream();
  return true;
  //...
}

public String readMessage() { 
  //...
  return bufferedReader.readLine();
  //...
}

protected Socket createSocket() //...
protected PrintStream createPrintStream() //...
protected BufferedReader createInputStream() //...


Veja que os métodos que criam os objetos são protected, isso é necessário pois o Mockito não consegue interceptar métodos que são privados.

O teste da classe ficará assim:

import static org.mockito.Mockito.*;
import static org.junit.Asserts.*;

@Test public void shouldReadMessage() throws UnknownHostException, IOException {
  //cria instancia da classe a ser testada
  Connection connection = new Connection("127.0.0.1", 14000);
  //transforma em um spy
  connection = spy(connection);
  //cria os mocks que serão retornados 
  //pelos metodos de criação de objetos 
  Socket socket = mock(Socket.class);
  BufferedReader bufferedReader = mock(BufferedReader.class);
  PrintStream printStream = mock(PrintStream.class);

  //define o comportamento dos métodos 
  doReturn(socket).when(connection).createSocket();
  doReturn(bufferedReader).when(connection).createInputStream();
  doReturn(printStream).when(connection).createPrintStream();
  when(bufferedReader.readLine()).thenReturn("readed from mock");

  connection.connect();
  assertNotNull(connection.readMessage());
}

Veja que apesar de termos que alterar a nossa classe e extrair as criações de objetos para métodos protected, foi possível criar um teste do método readMessage usando mocks dos objetos do socket.

O uso de Spies da maneira que foi mostrada, pode indicar que seu código esteja com um acoplamento alto e como sempre cabe pensar um pouco se não existe um jeito melhor de programar sua classe para evitar isto.

Isso conclui nossa série de posts sobre o Mockito, pelo menos por enquanto, espero muito que a pequena introdução que escrevi aqui os ajudem a criar seus testes unitários assim como tem me ajudado bastante.

Segue os links que usei de referência
Mocking object creation
Mockito class javadoc
Site do Mockito

Código fonte: https://github.com/diegoy/talks-mockito

Qualquer dúvida, como sempre perguntem, farei o possível para ajudar.