Рубрики
Без рубрики

Создайте юнит-тесты, удобнее и быстрее с Спок

Тесты и технические характеристики почти всегда являются головной болью для разработчиков. Мы тратим очень много времени… Помечено как bdd, учебное пособие, тестирование, java.

Тесты и технические характеристики почти всегда являются головной болью для разработчиков. Мы тратим много времени на написание тестов, иногда гораздо больше времени, чем мы тратим развития. Трудно также мы будем придерживаться исключительно в язык бизнеса, и написать что-то значительное. А проще-это поддержать нас в ожидаемых результатов кода. Таким образом, любая альтернатива, чтобы сосредоточиться на бизнес и сделать тестирование более быстро действует.

Спок, средство, – это концепция, которая приносит силу Groovy , чтобы сделать тестирование, в целом, более выразительные, более быстрые писать, приятно читать и понимать, выполнив, таким образом, более описательный звуки шагов спецификация четыре этапа.

Цель этой статьи сделать сканирование поверхности из функций этого framework.

Код, используемый в данном материале и доступен; здесь .

Службы

Создадим проект без архетип через Maven и мы добавим плагин GMaven , необходимое для интеграции Groovy в Maven, и Byte Buddy , без зависимостей, необходимых для создания Мокс.


    4.0.0
    com.leandro
    spock-maven-test
    0.0.1-SNAPSHOT
    Projeto para estudar como aplicar testes com Spock.

    
        11
        11
    

    
        
            org.spockframework
            spock-core
            2.0-M2-groovy-3.0
            test
        
        
            org.junit.jupiter
            junit-jupiter-engine
            5.6.2
            test
        
        
        
            net.bytebuddy
            byte-buddy
            1.10.13
            test
        
        
        
            com.h2database
            h2
            1.4.200
            test
        

        
        
            com.athaydes
            spock-reports
            2.0-RC2
            test
            
            
                
                    *
                    *
                
            
        
        
        
            org.slf4j
            slf4j-api
            1.7.30
            test
        
        
            org.slf4j
            slf4j-simple
            1.7.30
            test
        
    

    
        
            
            
                org.codehaus.gmavenplus
                gmavenplus-plugin
                1.9.0
                
                    
                        
                            compileTests
                        
                    
                
            
            
                maven-surefire-plugin
                3.0.0-M5
                
                    false
                    
                        **/*Test.java
                        **/*Spec.java
                    
                
            
        
    

Следует сравнительного между Байтовый приятель e o CGLIB.

Os estas usando o Spock serão колокадо на пасте src/тест/заводной*. *

Давайте начнем чувствуя вкус framework создание простого теста, но имеет немного силы выражения:

import spock.lang.Specification
import spock.lang.Unroll

class MaxTest extends Specification {
    @Unroll
    def "Verificar se o máximo entre #a e #b é #c"() {
        expect:
        Math.max(a, b) == c

        where:
        a  | b | c
        1  | 2 | 2
        5  | 2 | 5
        0  | 1 | 1
        -1 | 1 | 1
    }
}

Данных трех чисел a, b и c, мы хотим проверить, если наше приложение, которое вычисляет максимум между a и b, возвращает c для каждого случая в таблице.

Ожидаемые результаты являются эти:

С Спок, мы пишем спецификации, и для этого расширяем класс Specification . Спецификация состоит из

class Especificacao extends Specification {
  // fields
  // fixture methods
  // feature methods
  // helper methods
}

Fields

Эквивалентны полям классов Java, переменные с областью действия класса. Тем не менее, есть разница, запуск вместе декларации эквивалентно запускает их в один метод, аннотированный с @BeforeEach . Framework делает это таким образом, чтобы изолировать методы. Это хорошая практика, инициализирует поля в том же месте заявления.

Поля, переменные области действия класса с отдельных экземпляров в область действия метода.

Когда мы очень больших объектов, которые требуется много обработки, которые будут созданы, удобно держать в одном экземпляре и разделяет ее между методами тестирования. Для этого, Спок использует аннотации @Shared .

Таким образом, мы надеемся, что следующие испытания будут успешно выполнены:

import spock.lang.Shared
import spock.lang.Specification;

class CamposTest extends Specification {
    def normal = 5
    @Shared shared = 3

    def "Se alterar o valor do campo, deve ser verificável"() {
        given: 'Valor dos campos foram alterados'
        normal = 6
        shared = 4

        expect: 'O novo valor seja observado no resultado'
        normal == 6
        shared == 4
    }

    def "Sem qualquer alteração, mantem o valor do campo alterado anteriormente"() {
        given: 'Não há novas alterações nos campos'
        expect: 'Campo normal deve ter o valor antigo'
        normal == 5
        and: 'Campo shared deve ter o valor definido no método anterior'
        shared == 4
    }
}

Оскорблять возможности пользователя статические конечные поля . Документация предполагает отдать предпочтение @Shared и пусть в данном случае только для констант, вопрос семантики.

Методы Крепления

Отвечают за настройки среды тестирования, инициализация и завершение испытаний. Они:

// Equivale ao @BeforeAll/@BeforeClass do JUnit. Roda apenas uma vez // antes que os testes se iniciem.
def setupSpec() {}    
// Equivale ao @Before do JUnit. Roda antes de cada método.
def setup() {}    
// Equivale ao @After do JUnit. Roda logo após cada método.    
def cleanup() {}     
// Equivale ao @AfterAll/AfterClass do JUnit. Roda uma única vez ao //término do último teste.
def cleanupSpec() {}

Методы функций

Сердце все! Описывают спецификации, поведение ожидается от его применения. Методы, названные фразы говорят сами за себя и состоят из блоков с указанием этапов спецификация:

  1. GIVEN :, где вы делаете запуска тестирования, то есть, инициализации переменных. Может быть неявной, то есть, не должны быть объявлены.

  2. WHEN : представляет собой стимул для тестирования, один вход, внешнего действия. Всегда случается, что стимул, как мы надеемся, то ( then ) ответа.

  3. THEN : ответ на стимул, что ожидать после блока when . По сути, эти блоки возникают вместе, и может быть более одной пары when/then метод. Таким же образом, что expect этот блок всегда это результат условий. Мы будем рассматривать условия, еще впереди.

  4. EXPECT : вроде then , является более естественным для случаев, где это не интересно использовать блок when . Это происходит особенно в методах сугубо функциональных, то есть, без побочных эффектов. Присоединение стимул испытания в ответ на одно выражение, имеем презентации более лаконичным.

  5. CLEANUP : – подходящее место, чтобы завершить использование ресурсов, таких как доступ к файлам, streams, etc.

  6. WHERE : используется для написания тестов с параметрами. Каждый набор данных в блоке where рассматривается как новое испытание.

Условия

Явных или подразумеваемых,

Условий или assertions , испытывают их применения. Elac pode ser implícitas, dentro dos blocos когда/тогда ou dentro do bloco ожидать . Все объявленные внутри этих блоков составляют одно или более условий. Они также могут быть явными, из этих блоков. Для этого используется ключевое слово assert , пример.: утвердить стек.пустой .

Исключение

Обрабатывают случаях ожидается, исключение протестированного кода. Для проверки исключения, используют методы throw() e не брошен() .

import spock.lang.Specification

import static java.util.Collections.emptyList

class ExcecaoTest extends Specification {

    def "Lançar exceção quando tentar tirar um elemento de uma lista vazia"() {
        given: 'Uma lista vazia'
        def lista = emptyList()

        when: 'Tentar remover um elemento desta lista'
        lista.pop()

        then: 'Deve lançar exceção do tipo NoSuchElementException'
        def e = thrown(NoSuchElementException)
        and: 'Causa deve ser nula'
        e.cause == null
    }

    def "Não lançar exceção quando tentar incluir uma chave nula num mapa"() {
        given: 'Um hashmap'
        def mapa = new HashMap()

        when: 'Tentar inserir um par chave/valor com chave nula'
        mapa.put(null, 1)

        then: 'Não deve ocorrer qualquer exceção'
        noExceptionThrown()
    }

    def "Groovy == equivale a equals em Java e é nullsafe"() {
        when: 'Comparar null com qualquer objeto'
        def isLeiaNula = "General Leia Organa" == null
        def isHanNulo = null == "Han Solo"
        then: 'Deve retornar false e não gerar exceção de null pointer'
        !isLeiaNula && !isHanNulo
        notThrown(NullPointerException)
    }
}

Можно получить доступ к содержимому исключение, назначив результат метода в переменную, def(EmptyStackException) ou ainda, EmptyStackException() .

Методы помощи

С помощью других фреймворков модульного тестирования является общий перенять методы утилиты чтобы не дублировать код. Узел Groovy не будет по-другому. В следующем примере показано, силе языка.

import spock.lang.Specification

class HelpMethodsTest extends Specification {

    def "Verificar se email obedece padrão"() {
        expect: 'Meu email fake seja válido!'
        "é um email válido"("leandro@leandro.com.br")
    }

    static def "é um email válido"(email) {
        email.contains('@') && email.endsWith(".com.br")
    }
}

Чтобы лучше определить, где проблема, возможно ли использовать assert внутри метода утилита для каждой условной линии.

С помощью метода with для проверки полей

O Groovy предлагает очень большой ассортимент syntactic sugars, которые позволяют нам сделать код более простым. Одним из них является метод with , что позволяет тестировать свойства объекта в несколько строк, пока любой из этих свойств является ложным. Альтернатива with verifyAll , который проверяет все поля, без короткого замыкания.

import groovy.transform.ToString
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll

@Unroll
class WithTest extends Specification {

    @Shared
            mulheresImportantes = [
                    "Ada Lovelace",
                    "Grace Hopper",
                    "Margaret Hamilton",
                    "Katie Bouman",
                    "Barbara Liskov"
            ]

    @ToString
    static class PessoasImportanteDaTI {
        String nome
        String sexo
        String descricao
    }


    def "Verificar se #pessoa.nome é uma mulher importante das ciências da computação"() {
        verifyAll(pessoa) {
            nome in mulheresImportantes
            sexo == 'F'
        }

        where:
        pessoa << [
                new PessoasImportanteDaTI(
                        nome: "Ada Lovelace",
                        sexo: 'F',
                        descricao: "Escreveu o primeiro algoritmo\
                    para ser processado por uma máquina. "),
                new PessoasImportanteDaTI(
                        nome: "Grace Hopper",
                        sexo: 'F',
                        descricao: "Criou uma linguagem que foi a \
                    base para o COBOL."),
                new PessoasImportanteDaTI(
                        nome: "Katie Bouman",
                        sexo: 'F',
                        descricao: "Criou algotimos para processar \
                    a primeira imagem de um buraco negro."),
                new PessoasImportanteDaTI(
                        nome: "Barbara Liskov",
                        sexo: 'F',
                        descricao: "Criou o Princípio da Substituição de Liskov, \
                    foi a primeira mulher a obter um PhD em Ciência da Computação \
                    nos Estados Unidos e inventou o Tipo Abstrato de Dado."),
                new PessoasImportanteDaTI(
                        nome: "Margaret Hamilton",
                        sexo: 'F',
                        descricao: "Desenvolveu o programa de voo usado no projeto Apollo 11."),
                new PessoasImportanteDaTI(
                        nome: "Dennis Ritchie",
                        sexo: 'M',
                        descricao: "Criou a linguagem de programação C."),
        ]
    }
}

Esse é o resultado esperado do test acima utilizando библиотека Расширение отчета Спока .

Вот и наш нарушитель!

Mocking

Тест за Единицу товара может быть только называется так изолирована от внешнего мира, это значит, испытывает только тот блок кода, минимальные и мок зависимости. Спок предлагает средства, достаточно элегантные, чтобы сделать это.

import groovy.sql.Sql
import groovy.transform.EqualsAndHashCode
import spock.lang.Specification

class MockTest extends Specification {

    @EqualsAndHashCode
    class Carta {
        Integer id
        String nome
    }

    class MagicRepository {
        Sql sql

        MagicRepository() {
            sql = Sql.newInstance("jdbc:h2:mem:", "org.h2.Driver")
            sql.execute '''
              CREATE TABLE carta (
                id INTEGER PRIMARY KEY AUTO_INCREMENT, 
                nome VARCHAR(64)
              )'''
        }

        Carta addCarta(nome) {
            sql.execute "INSERT INTO carta(nome) VALUES ${nome}"
            findByNome(nome)
        }

        Carta findById(Integer id) {
            mapCarta sql.firstRow("select * from carta where id = ${id}")
        }

        Carta findByNome(String nome) {
            mapCarta sql.firstRow("select * from carta where nome = ${nome}")
        }

        private Carta mapCarta(retorno) {
            new Carta().tap {
                id = retorno.ID
                nome = retorno.NOME
            }
        }
    }

    class MagicService {
        MagicRepository repository

        Carta findById(Integer id) { repository.findById(id) }
    }

    def 'Stub permite apenas definir comportamento de métodos'() {
        given: 'Repository é apenas um stub'
        def carta = new Carta(nome: 'Mana Crypt')
        def stubRepository = Stub(MagicRepository) {
            findById(_ as Integer) >> { Integer id -> carta.tap { id: id } }
        }
        def service = new MagicService(repository: stubRepository)

        expect: 'O carta encontrada deve ser a carta do stub'
        service.findById(1) == carta.tap { id: 1 }
    }

    def "Spy permite saber quantas interações ocorreram e o retorno do método espiado"() {
        given:
        def esperada1 = new Carta(id: 1, nome: 'Black Lotus')
        def esperada2 = new Carta(id: 2, nome: 'Mana Crypt')

        def spyRepository = Spy(MagicRepository) {
            findById(2) >> esperada2
        }
        spyRepository.addCarta("Black Lotus")
        spyRepository.addCarta("Mox Pearl")

        def service = new MagicService(repository: spyRepository)

        when:
        def carta1 = service.findById(1)
        then:
        carta1 == esperada1
        spyRepository.findById(1) == esperada1
        1 * spyRepository.findById(1)

        when:
        def carta2 = service.findById(2)
        then:
        carta2 == esperada2
        spyRepository.findById(2) == esperada2
    }

    def "Mock pode tanto ver a quantidade de interações \
        quanto redefinir comportamento do método"() {
        given: 'Repositório será chamado três vezes'

        def esperada1 = new Carta(id: 1, nome: 'Mox Pearl')
        def esperada2 = new Carta(id: 2, nome: 'Mox Emerald')
        def mockRepository = Mock(MagicRepository) {
            2 * findById(1) >>> [esperada1, esperada2]
            1 * findById(2) >> esperada2
        }

        def service = new MagicService(repository: mockRepository)
        when:
        def carta1 = service.findById(1)
        def carta2 = service.findById(1)
        def carta3 = service.findById(2)
        then:
        carta1 == esperada1
        carta2 == esperada2
        carta3 == esperada2
    }
}


Спок позволяет использовать три категории мок:

  1. Мок – : Переопределяет поведение в классе, когда она вызывается, и позволяет проверить, как она используется. Это самый известный и имеет акцент на определение “договор”, как он должен быть назван, что должен отвечать и как часто.

  2. Шпион : Не переопределяет поведение, но она позволяет проверить, когда фактический метод класса вызывается, аргументы и результаты;

  3. Заглушки : Только поведение, но и не позволяет проверить вызовы и заглушки не протестировать этот бесплатный. Черный ящик, который не хочет называться, но если это так, возвращает ответ по умолчанию.

Чтобы продолжить обучение…

Оригинал: “https://dev.to/leandrostl/crie-testes-unitarios-mais-amigaveis-e-mais-rapidamente-com-spock-4bgi”