writing unit tests with spock framework
Escrevendo testes de unidade com Spock Framework: acessórios de teste, afirmações e relatórios
Nisso Guia completo para iniciantes em Spock , Uma carta Introdução ao Spock Framework e à programação Groovy foi dado em nosso tutorial anterior.
Neste tutorial, vamos percorrer todos os detalhes / etapas necessárias para começar Teste de unidade em Spock.
melhor aplicativo para baixar músicas mp3
Para fins de simplicidade, vamos testar um aplicativo de calculadora simples que possui diferentes métodos, como adição, subtração, multiplicação, divisão, etc., todos os quais aceitam parâmetros inteiros e retornam uma saída inteira.
O que você aprenderá:
- Teste de unidade com tutorial de vídeo do Spock
- Começando
- Palavra-chave “def”
- O ciclo de vida de uma especificação Spock
- Asserções Spock
- Comunicando
- Conclusão
- Leitura recomendada
Teste de unidade com tutorial de vídeo do Spock
Começando
Semelhante a qualquer outra estrutura de teste de unidade, Spock também pode ser usado para escrever cenários / casos de teste para um aplicativo em teste. Tentaremos comparar e contrastar os diferentes recursos da estrutura Spock com as estruturas existentes / conhecidas como JUnit .
Palavra-chave “def”
Vamos primeiro tentar entender a palavra-chave 'def' do Groovy em poucas palavras. A palavra-chave def é usada para definir o tipo-def e pode ser usada para declarar uma função, bem como um campo / variável.
“Def” é geralmente usado quando não queremos restringir o tipo de um campo ou tipo de retorno de um método. Vejamos alguns exemplos da palavra-chave def em uma classe bacana e todos os seus usos válidos.
// def as variable types def inputNum = 100 def inputStr = 'hello world!!' def app = new CalculatorApp() // def as return type of function def 'test function'() { // function body here }
O ciclo de vida de uma especificação Spock
A especificação do Spock quando executada procura todos os testes definidos e os executa um por um. No entanto, existem poucas outras funcionalidades / recursos fornecidos pelo Spock para tornar os testes menos redundantes e mais legíveis.
Vamos discutir alguns recursos abaixo:
Definindo entradas / variáveis como parte das especificações
Considere ter vários testes, todos usando os mesmos valores de entrada. Uma forma seria inicializar os valores de entrada em cada teste individualmente, senão podemos definir diretamente os campos no nível de especificação e garantir que antes de cada teste, os campos serão inicializados e estarão disponíveis para o teste que está sendo executado.
Vamos ver um exemplo para nossa classe de aplicativo de calculadora .
Definiremos os dados de entrada no nível da especificação para que estejam disponíveis com os valores iniciais para todos os testes presentes na especificação.
class CalculatorAppSpec extends Specification { def input1 = 50 def input2 = 10 def result = 0 def app = new CalculatorApp() def 'addition with valid inputs return expected result'() { when: result = app.add(input1, input2) then: result == 60 } def 'multiplication with valid inputs return expected result'() { when: result = app.multiply(input1, input2) then: result == 500 } def 'division with valid inputs return expected result'() { when: result = app.divide(input1, input2) then: result == 5 } def 'subsctraction with valid inputs return expected result'() { when: result = app.substract(input1, input2) then: result == 40 } }
Neste exemplo de código, você pode ver que definimos input1, input2, o aplicativo em teste e o resultado no nível de especificação. O que isso garante é que toda vez que um teste for executado a partir dos arquivos de especificação, os campos inicializados sejam passados para esse teste. Isso realmente elimina a necessidade de configurar testes sempre com valores de entrada.
Dispositivos de teste
Semelhante à maioria das estruturas de teste de unidade, Spock também fornece métodos de configuração e limpeza para executar lógicas / tarefas especiais em eventos de ciclo de vida específicos de execução de teste.
setupSpec & cleanupSpec
Esses métodos são chamados uma vez para cada execução de Spec e são chamados antes e depois da execução do teste, respectivamente. Estes são comparáveis a @ Antes da aula e @ Depois da aula anotações de JUnit.
configuração e limpeza
Esses métodos são chamados antes e depois da execução de cada teste na especificação.
Esses ganchos são o lugar certo para qualquer lógica / parte do código que você gostaria de executar antes e depois da execução do teste. Por exemplo , Na limpeza, você pode escrever um código para fechar a conexão do banco de dados (se houver) que foi usada durante o teste.
Eles podem ser comparados a @ BeforeTest e @ AfterTest anotações no JUnit.
Vamos ver um exemplo dessas luminárias em nosso teste de aplicação de calculadora.
def setupSpec() { println('###in setup spec!') } def cleanupSpec() { println('###in cleanup spec!') } def setup() { println('>>>in test setup!') } def cleanup() { println('>>>in test cleanup!') }
Se o código do dispositivo de teste acima for adicionado a uma especificação contendo 4 testes, a saída será a seguinte:
ferramentas de teste de desempenho para aplicativos Java
###in setup spec! >>>in test setup! >>>in test cleanup! >>>in test setup! >>>in test cleanup! >>>in test setup! >>>in test cleanup! >>>in test setup! >>>in test cleanup! ###in cleanup spec!
Asserções Spock
As afirmações em Spock são chamadas de afirmação de poder (e foi adotada por groovy mais tarde, após ser introduzida por Spock). As asserções de Spock fornecem muitas exceções de diagnóstico no caso de qualquer falha de declaração.
Pode-se descobrir facilmente o que deu errado simplesmente olhando para o diagnóstico de falha em vez de verboso AssertionErrors no JUnit e em outras estruturas.
Vamos tentar entender isso com um exemplo e compará-lo com JUnit
Vamos trabalhar com um teste simples que verifica a igualdade da string e ver quais diagnósticos são gerados em caso de falha de asserção.
Teste Spock
def 'check case-insensitive equality of 2 strings'() { given: 'two input strings' String str1 = 'hello' String str2 = 'HELLO world' when: 'strings are lowercased' str1 = str1.toLowerCase() str2 = str2.toLowerCase() then: 'equal strings should return success' str1 == str2 }
Teste JUnit
@Test public void compareStrings_withValidInput_shouldReturnSuccess() { // Arrange String str1 = 'hello'; String str2 = 'HELLO world'; // Act str1 = str1.toLowerCase(); str2 = str2.toLowerCase(); // Assert Assert.assertEquals(str1,str2); }
Saída de Spock
Condition not satisfied: str1 == str2 | | | hello| hello world false 6 differences (45% similarity) hello(------) hello( world) Expected :hello world Actual :hello
Saída JUnit
org.junit.ComparisonFailure: Expected :hello Actual :hello world
Como você pode inferir acima, as informações de diagnóstico fornecidas por Spock têm melhores detalhes e são mais fáceis de usar quando comparadas a outras estruturas como JUnit.
Dicas e truques para afirmações
Afirmando vários elementos de uma vez - Spock fornece várias abreviações para afirmações e uma delas é a notação '*', que permite afirmar os elementos da lista.
Vamos entender isso com um exemplo:
Considere uma classe CityInfo tendo cityName e população como os campos. Vamos escrever um teste de Spock para afirmar os nomes das cidades que estão na lista fornecida.
public class CityInfo { public CityInfo(String cityName, int population) { this.cityName = cityName; this.population = population; } public String cityName; public int population; }
Vamos ver o teste agora:
def 'Assert multiple elements of list' () { given: def cityList = new LinkedList() cityList.add(new CityInfo('Mumbai', 120)) cityList.add(new CityInfo('Delhi', 80)) cityList.add(new CityInfo('Chennai', 100)) expect: cityList*.cityName == ('Mumbai', 'Delhi', 'Chennai') }
Conforme mostrado no atalho de afirmação acima, você pode validar a lista inteira com a ajuda da palavra-chave “*”.
Vejamos também como seria uma falha. Vou remover o nome de qualquer cidade da afirmação acima.
Condition not satisfied: cityList*.cityName == ('Delhi', 'Chennai') | | | | | false | (Mumbai, Delhi, Chennai) (app.CityInfo@31368b99, app.CityInfo@1725dc0f, app.CityInfo@3911c2a7)
Você pode ver que as informações de diagnóstico de falha de asserção são ricas e fáceis de compreender.
Aproveitando o parâmetro de fechamento - every ().
Vamos ver como podemos alavancar o parâmetro de fechamento denominado every () para adicionar uma declaração para cada elemento de uma lista ou coleção. No mesmo exemplo, vamos tentar adicionar uma afirmação que valida a população de cada cidade se a entrada fornecida for> 50.
def 'Assert multiple elements of list' () { given: def cityList = new LinkedList() cityList.add(new CityInfo('Mumbai', 120)) cityList.add(new CityInfo('Delhi', 80)) cityList.add(new CityInfo('Chennai', 100)) expect: cityList*.cityName == ('Mumbai', 'Delhi', 'Chennai') and: cityList.population.every() { it > 50 } }
Afirmando exceções lançadas
As exceções podem ser declaradas para serem lançadas no bloco “then” (o que significa quando o bloco também é necessário). O detalhe da exceção pode ser diagnosticado atribuindo a exceção lançada a um campo e declarando as propriedades necessárias da exceção lançada.
Vamos usar a mesma classe CityInfo e definir um método que lança uma exceção e escrever um teste para ele.
public class CityInfo { public CityInfo(String cityName, int population) { this.cityName = cityName; this.population = population; } public String cityName; public int population; public CityInfo() { } public int getCleanlinessScore() { throw new RuntimeException('method not implemented'); } }
Vamos dar uma olhada no teste agora:
def 'cleanliness score throws runtime exception with message - method not implemented'() { given: CityInfo app = new CityInfo(); when: app.cleanlinessScore() then: def e = thrown(RuntimeException) e.message == 'method not implemented' }
Comunicando
Para gerar relatórios baseados em HTML bonitos e detalhados, existem bibliotecas disponíveis que podem ser adicionadas no arquivo de construção e agora sempre que os testes forem executados durante a construção (ou por execução direta), um relatório baseado em HTML detalhado será gerado no pasta de saída.
Para obter os relatórios de teste gerados, adicione as seguintes bibliotecas ao arquivo build.gradle (e da mesma forma para o arquivo Maven pom.xml).
testCompile 'com.athaydes:spock-reports:1.6.1' testCompile 'org.slf4j:slf4j-api:1.7.13' testCompile 'org.slf4j:slf4j-simple:1.7.13'
Agora construa o projeto e execute os testes executando todos os testes na pasta “test” ou executando “ teste de limpeza gradle ”.
Você pode abrir index.html arquivo para obter um relatório resumido de todas as especificações do Spock disponíveis para execução.
diferenças c vs c ++
Se você quiser ver o relatório detalhado para uma especificação específica, clique na especificação da lista acima e poderá ver um relatório detalhado de falhas e sucessos.
Conclusão
Neste tutorial, cobrimos os fundamentos do teste de unidade com o Spock Framework. Vimos as diferentes formas e atalhos para escrever asserções e o tipo de informação rica de diagnósticos gerada pela estrutura Spock para falhas de asserção.
Também vimos como poderíamos gerar relatórios bastante silenciosos baseados em HTML para os testes Spock, que incluem os mesmos diagnósticos detalhados para os testes executados.
Nosso próximo tutorial irá informá-lo sobre como escrever testes parametrizados com Spock em detalhes !!
PREV Tutorial | PRÓXIMO Tutorial
Leitura recomendada
- Teste baseado em dados ou parametrizado com Spock Framework
- Perguntas da entrevista de Spock com respostas (mais populares)
- Spock para integração e testes funcionais com selênio
- Spock Mocking e Stubbing (exemplos com tutoriais em vídeo)
- Tutorial do Spock: Testando com Spock e Groovy
- Tutorial Mockito: Framework Mockito para simulação em testes de unidade
- As diferenças entre teste de unidade, teste de integração e teste funcional
- Chave para o teste de unidade de sucesso - como os desenvolvedores testam seu próprio código?