Spock Framework: Teste Java com mais produtividade
Tags: java, groovy, test, spockframework
Published: 2020-01-15 03:13:17

---
title: Spock Framework: Teste Java com mais produtividade
published: true
description: Transforme a prática de escrever teste numa tarefa mais prazerosa
tags: java, groovy, test, spockframework
cover_image: https://res.cloudinary.com/mariorez/image/upload/v1579089916/steve-halama-Yhc7YGZlz3g-unsplash-2_rmc6k7.jpg
---

Quem já escreve testes há algum tempo deve ter percebido que, para cada funcionalidade da aplicação que queremos testar, temos que escrever ao menos um cenário que cobre o “caminho feliz” e um ou mais cenários que cobrem os “caminhos infelizes”. Escrevemos muito mais código na suíte de testes em comparação ao código que será executado de fato em produção.

Tendo isso em vista, a ferramenta que utilizamos para dar suporte a criação de testes têm impacto significativo na velocidade de desenvolvimento, manutenção e evolução da aplicação.

> **Caminho feliz**: cenário onde o cliente utiliza a aplicação de acordo com as especificações do projeto.
> **Caminho infeliz**: são as possibilidades de utilização incorretas da aplicação pelo cliente (exceções).

Neste artigo vou apresentar o [**Spock**](http://spockframework.org/spock/docs/1.3/all_in_one.html), um framework de **testes** para aplicações **Java** e **Groovy**, e como sua linguagem de especificação elegante e expressiva pode trazer maior produtividade no dia-a-dia do programador.

## Analisaremos o Spock através das seguintes características:

-   Legibilidade e organização do teste
-   Versatilidade de parametrização do teste
-   Reportando falhas
-   Testando a interação entre objetos: “*Mocking*” e “*Stubbing*”
-   Trabalhando com exceções

## Legibilidade e organização do teste

Praticamente todo teste que escrevemos (salvo algumas exceções) seguem as seguintes [fases](http://xunitpatterns.com/Four%20Phase%20Test.html):

1.  **Configuração**: onde inicializamos os dados que utilizaremos no teste.
2.  **Execução**: onde o objeto que está sendo testado será executado.
3.  **Verificação**: onde avaliamos os resultados retornados na fase de execução.

Vamos exemplificar essas fases escrevendo um cenário que testará uma funcionalidade fictícia de cadastro de usuário com [JUnit 5](https://junit.org/junit5/):

{% gist https://gist.github.com/mariorez/81ac5e3de6a7c9ca9468fd95d08f1b3b %}

Embora essas fases estejam presentes na maioria dos testes, nem todas as bibliotecas dispoẽm de mecanismos para deixar isso de forma explícita; no caso do JUnit costumo usar comentários para demarcar cada fase.

Reescrevendo esse mesmo cenário com o Spock o teste fica bem mais legível e organizado, graças a linguagem [Groovy](http://groovy-lang.org/) (base do Spock) que nos permite escrever os métodos de [forma declarativa](http://spockframework.org/spock/docs/1.3/all_in_one.html#_feature_methods) e manter o corpo do teste mais simples e conciso. O Spock impõe a separação das fases ao estilo [BDD](https://en.wikipedia.org/wiki/Behavior-driven_development), utilizando os [blocos](http://spockframework.org/spock/docs/1.3/all_in_one.html#_blocks): “***given***”, “***when***” e “***then***”.

{% gist https://gist.github.com/mariorez/7ede0e2705ae8408935bcd19229c1be8 %}

A fase de verificação também fica mais simples, pois utiliza os mesmos operadores de comparação do Java (removendo a necessidade de conhecer os métodos de “[*assertion*](https://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions)”):

{% gist https://gist.github.com/mariorez/89afc1109068b3dc487ceb6ec5a9393f %}

> **Dica**: existe também o bloco “***and***” que pode ser usado para deixar a [especificação](http://spockframework.org/spock/docs/1.3/all_in_one.html#specifications_as_documentation) do teste mais compreensível:

{% gist https://gist.github.com/mariorez/5e5894f4302004b2361ee8b88942db45 %}

# Versatilidade de parametrização do teste

Com frequência precisamos executar o teste com dados diferentes para garantir que todos os cenários possíveis estão sendo validados.

O Spock resolve esse problema através do bloco “*where*” onde podemos prover uma lista de dados a serem utilizados em nossos testes.

No exemplo abaixo utilizamos o “[*Data Tables*](http://spockframework.org/spock/docs/1.3/all_in_one.html#data-tables)” para fornecer os dados de entrada ao método do teste. A primeira linha da tabela (*header*) declara o nome das variáveis que utilizaremos no teste, e as demais linhas correspondem ao valor dessas variáveis. Para cada linha o método do teste será executado uma vez.

{% gist https://gist.github.com/mariorez/b0015ebd1bf3f0fb1f217657f7356be7 %}

Organizando os dados no formato de tabela facilita tanto para quem escreve, como também para quem lê o teste; fazendo com que o teste sirva como uma documentação do funcionamento do objeto sob teste.

O Spock utiliza o conceito de “[*Data Driven Testing*](http://spockframework.org/spock/docs/1.3/all_in_one.html#_data_driven_testing)”, que fornece diversas formas de gerarmos dados de entrada, dando mais flexibilidade ao programador durante a escrita dos testes:

-   [Data Tables](http://spockframework.org/spock/docs/1.3/all_in_one.html#data-tables)
-   [Data Pipes](http://spockframework.org/spock/docs/1.3/all_in_one.html#_data_pipes)
-   [Multi-Variable Data Pipes](http://spockframework.org/spock/docs/1.3/all_in_one.html#_multi_variable_data_pipes)
-   [Data Variable Assignment](http://spockframework.org/spock/docs/1.3/all_in_one.html#_data_variable_assignment)
-   [Combinação dos itens acima](http://spockframework.org/spock/docs/1.3/all_in_one.html#_combining_data_tables_data_pipes_and_variable_assignments)

# Reportando falhas

No teste abaixo inserimos uma falha propositalmente na tabela do bloco “where” (linha 9):

{% gist https://gist.github.com/mariorez/f2a713801438739a54eaf611f3717dce %}

> **Dica**: prefira utilizar o bloco “[*expect*](http://spockframework.org/spock/docs/1.3/all_in_one.html#_expect_blocks)” quando a execução e a validação podem ser descritas numa única fase.

Ao executar o teste teremos a seguinte saída no terminal:

{% gist https://gist.github.com/mariorez/bad1337483f85768e4d7158cf9a09e70 %}

Com Spock é fácil de identificar que a falha ocorreu na segunda interação, mas podemos deixar essa informação ainda mais explícita utilizando a anotação de “[@Unroll](http://spockframework.org/spock/docs/1.3/all_in_one.html#_method_unrolling)”:

{% gist https://gist.github.com/mariorez/8432da3b3d66b7a7a699112bc2ece857 %}

Perceba que inserimos marcações no nome do método usando o sinal “#” mais o nome das variáveis utilizadas no teste. Executando o teste novamente teremos a seguinte saída:

{% gist https://gist.github.com/mariorez/a67aed8cd91175a7ff9fea90e65b17cc %}

Identificar rapidamente onde ocorreu a falha aumenta a nossa produtividade, principalmente numa suíte de teste muito grande.

# Testando a interação entre objetos

## *Mocking*

Nem só de verificação de estado vivem nossos testes; há cenários onde é necessário explorar o comportamento do objeto sob teste do ponto de vista das interações que ele faz com outros objetos.

Nesse tipo de teste (também conhecido como “*Interaction Tests*” ou “*Collaboration Test*”) utilizamos a técnica de “[*Mock*](http://xunitpatterns.com/Mock%2520Object.html)” (imitar) para conseguirmos analisar as interações entre os objetos sob teste.

A [criação de “](http://spockframework.org/spock/docs/1.3/all_in_one.html#_creating_mock_objects)[*Mocks*](http://spockframework.org/spock/docs/1.3/all_in_one.html#_creating_mock_objects)[”](http://spockframework.org/spock/docs/1.3/all_in_one.html#_creating_mock_objects) é muito simples e pode ser feita de duas formas:

{% gist https://gist.github.com/mariorez/0ff5c8d01b0f68cb37fc22f7385bb6a3 %}

Ou utilizando a sintaxe Java:

{% gist https://gist.github.com/mariorez/b8b33047b4c0e716c26293befcaa6f4d %}

Vejamos o uso do “*Mock*” mais detalhadamente no exemplo abaixo (linhas 14, 15):

{% gist https://gist.github.com/mariorez/608331d6c6414bf19d4e6211e693d3fb %}

Diferente dos exemplos anteriores, nos quais verificamos o estado do objeto sob teste (ex: `user.getName() == name`); nosso interesse agora é saber se após a fase execução (“*when*”) o objeto “*Publisher*” realizou as interações necessárias com os seus colaboradores “*Subscriber*”.

A análise das interações é feita através de restrições, ou seja, no exemplo acima esperamos que objeto alvo “*Subscriber*”, seja chamado através do método “*receive*”, recebendo exatamente como argumento a palavra “*hello*” e que isso ocorra apenas um vez.

Essas restrições estão organizadas no Spock da seguinte forma:

1.  restrição por cardinalidade
2.  restrição de alvo
3.  restrição de método
4.  restrição de argumento

{% gist https://gist.github.com/mariorez/28f615a4adce94bfeed0c75e5ac2d12b %}

Obs: todas as variações de restrição podem ser vistas na [documentação do Spock](http://spockframework.org/spock/docs/1.3/all_in_one.html#_interactions_2).

## *Stubbing*

Como podemos testar um objeto, de forma independente, quando ele depende do resultado retornado por outros objetos que colaboram com ele?

É preciso controlar os objetos de colaboração e definir como eles devem se comportar durante as interações com o objeto sob teste.

Chamamos essa técnica de “[*Stub*](http://xunitpatterns.com/Test%20Stub.html)”, onde substituímos o objeto real por um objeto que será alimentado com as entradas que desejamos utilizar no nosso testes.

Alteramos o nosso exemplo de teste que agora utiliza o “[*Stubbing*](http://spockframework.org/spock/docs/1.3/all_in_one.html#_stubbing)” para definir um resultado ao método “*receive*” da interface “*Subscriber*” (linha 22). Dessa forma podemos validar não apenas a interação mas também o retorno esperado:

{% gist https://gist.github.com/mariorez/e76e5e95aa1cc3e15bd413642134d02a %}

As interações com “*Stubs*” diferem um pouco em comparação as interações como “*Mocks*”, conforme demonstrado abaixo:

{% gist https://gist.github.com/mariorez/1fbd4f531c9219c8333662ef058c93d0 %}

Mais uma vez, todas as variações de interações pode ser vistas na [documentação do Spock](http://spockframework.org/spock/docs/1.3/all_in_one.html#_returning_fixed_values), especialmente a parte que trata da [combinação de “](http://spockframework.org/spock/docs/1.3/all_in_one.html#_combining_mocking_and_stubbing)[Mocking](http://spockframework.org/spock/docs/1.3/all_in_one.html#_combining_mocking_and_stubbing)[” e “](http://spockframework.org/spock/docs/1.3/all_in_one.html#_combining_mocking_and_stubbing)[Stubbing](http://spockframework.org/spock/docs/1.3/all_in_one.html#_combining_mocking_and_stubbing)[”](http://spockframework.org/spock/docs/1.3/all_in_one.html#_combining_mocking_and_stubbing).

# Trabalhando com exceções

Por fim, porém não menos importante, precisamos ser capazes de validar quando o objeto sob teste deve lançar uma exceção.

Geralmente, o uso de exceções nos testes ocorrem de duas formas:

1 - Validar a ocorrência de uma exceção (“[*Exception Conditions*](http://spockframework.org/spock/docs/1.3/all_in_one.html#_exception_conditions)”).

{% gist https://gist.github.com/mariorez/3d154cc4be62df768a959f775307f450 %}

2 - Simular uma exceção [como efeito colateral](http://spockframework.org/spock/docs/1.3/all_in_one.html#_performing_side_effects) (utilizando “*Stubs*”).

{% gist https://gist.github.com/mariorez/bfbeaa30916f9acf2e25e159a0fce62a %}

# Conclusão

Ainda como pontos positivos do Spock vale listar:

-   Spock disponibiliza um “[Web Console](http://meetspock.appspot.com/)” onde podemos experimentar o seu funcionamento.
-   [Projeto de exemplo](https://github.com/spockframework/spock-example) mostrando como configurar com: Ant, Gradle e Maven.
-   Ótima documentação, embasada em conceitos do [Agile](https://en.wikipedia.org/wiki/Agile_software_development) e [BDD](https://en.wikipedia.org/wiki/Behavior-driven_development).
-   O framework de “Mock” do Spock é integrado, ou seja, não é preciso importar outras bibliotecas (embora também seja possível utilizá-las em conjunto com o Spock).
-   [Integração com o Spring](http://spockframework.org/spock/docs/1.3/all_in_one.html#_spring_module).

Nem tudo são flores. Importante ressaltar que, em comparação, o [JUnit 5](https://junit.org/junit5/) ganha nos seguintes pontos:

-   100% implementado em Java.
-   Melhor compatibilidade com as últimas versões da JDK.
-   Melhor integração com as IDEs.

Enfim, desde que utilizei o Spock pela primeira vez não larguei mais; não apenas pelo ganho de produtividade, mas também pela forma como os testes passam a funcionar (de fato) como a documentação da aplicação.

Referências:

-   Why I prefer Spock over JUnit by Bouke Nijhuis ([YouTube](https://www.youtube.com/watch?v=aydVJbF7Mas))
-   Spock vs JUnit 5 - Clash of the Titans by Marcin Zajaczkowski ([YouTube](https://www.youtube.com/watch?v=AwX3i1yWDkc), [Slides](https://speakerdeck.com/szpak/spock-vs-junit-5-clash-of-the-titans))
-   XUnit Test Patterns - Refactoring Test Code by Gerard Meszaros ([Site](http://xunitpatterns.com/), [Book](https://www.amazon.com/gp/product/B004X1D36K))