Aula 4 - Familiarização com Spock Mocks

Mocks na Spock Framework

O objetivo desta aula é a familiarização com mocks na spock framework, um dos objetos de estudo na segunda entrega do projeto. Para informações mais detalhadas, consultar a documentação oficial. A inspiração para este tutorial está aqui.


1. Mocks na Spock Framework

Mocks são uma forma de simular o comportamento de uma classe necessária para a execução de um teste. É uma forma útil de se testar a lógica de um aplicação isoladamente das suas dependências. Para uma explicação mais aprofundada, ver o seguinte artigo.

A spock framework oferece funcionalidades para fazer mocking a classes. Para instanciar um mock, podemos fazer o seguinte:

PaymentGateway paymentGateway = Mock()

Neste exemplo, o tipo da nossa simulação é inferido pelo tipo de variável. Como o Groovy é uma linguagem dinâmica, também é possível fornecer um argumento de tipo, que nos permite não ter que atribuir à simulação qualquer tipo específico:

def paymentGateway = Mock(PaymentGateway)

Assim, sempre que chamamos um método do paymentgatewayuma resposta padrão será dada, sem invocar uma instância real da classe:

when:    
def result = paymentGateway.makePayment(12.99) 

then:    
result == false
Como não foram definidas as respostas padrão para os métodos da classe a simular, a spock framework decidirá o retorno com base no tipo do método (lenient mocking).

2. Simulação

É possível configurar métodos chamados no mock para responder de forma específica. Por exemplo, fazer com que o método makePayment do paymentgateway mock retorne true quando fazemos um pagamento de 20:
given:    
paymentGateway.makePayment(20) >> true 

when:    
def result = paymentGateway.makePayment(20) 

then:    
result == true

De notar que a spock framework faz uso de sobrecarga de operadores (operator overloading) do groovy para simular as invocações aos métodos da classe. Em Java, só seria permitido invocar métodos definidos na classe, sendo que o código resultante seria potencialmente menos expressivo e detalhado. 

A spock framework é bastante expressiva. Por exemplo, se makePayment retornar true para qualquer invocação, então podemos escrever o seguinte:


paymentGateway.makePayment(_) >> true

Se quiséssemos alternar entre diferentes respostas, poderíamos fornecer uma lista, para a qual cada elemento será devolvido em sequência:


paymentGateway.makePayment(_) >>> [true, true, false, true]

É recomendado que seja lida a documentação oficial para complementar a informação neste tutorial (que é, certamente, incompleta).

3. Verificação

Outra funcionalidade oferecida pela spock framework no que a mocks diz respeito, é permitir verificar que métodos foram invocados (incluindo a frequência). Um exemplo de uso em verificação é verificar se um método, com tipo de retorno void, no mock é invocado uma vez. Na spock framework, isso pode ser feito da seguinte forma.

def "Should verify notify was called"() {    
  given:        
  def notifier = Mock(Notifier)  
   
  when:        
  notifier.notify('foo')     

  then:        
  1 * notifier.notify('foo')
}

Neste exemplo, se o método não for invocado 1 vez, então o teste irá falhar. É possível também verificar se o método não foi invocado com um dado parametro:

2 * notifier.notify(!'foo')
3. Exercício

Nesta aula vamos utilizar a Spock framework num subsistema do sistema, bastante simplificado, Adventures. Para isso necessitamos de trabalhar numa versão do código preparada para esta aula, de modo a não interferir com o código que se encontra a ser desenvolvido para a entrega da primeira parte do projeto. Assim, cada aluno irá trabalhar no seu repositório pessoal no git:

  • Aceder, usando a interface do GitHub, ao repositório https://github.com/tecnico-softeng/aula-spock, e efetuar um fork (opção no canto superior direito)
  • Fazer git clone do seu repositório pessoal
  • Executar mvn install
  • Abrir o projeto no IDE como um projeto maven

De seguida, AdventureProcessMethodMockTest e crie uma nova classe, AdventureProcessMethodMockSpockTest, que teste o mesmo comportamento de sucesso usando mocks da spock framework. Acrescente depois os seguintes casos de teste:

  • A invocação de BankInterface.processPayment lança uma BankException
  • A invocação de HotelInterface.reserveHotel lança uma HotelException
  • A invocação de ActivityInterface.reserveActivity lança uma ActivityException


A solução deste lab pode ser encontrada no seguinte link.