Тесты и технические характеристики почти всегда являются головной болью для разработчиков. Мы тратим много времени на написание тестов, иногда гораздо больше времени, чем мы тратим развития. Трудно также мы будем придерживаться исключительно в язык бизнеса, и написать что-то значительное. А проще-это поддержать нас в ожидаемых результатов кода. Таким образом, любая альтернатива, чтобы сосредоточиться на бизнес и сделать тестирование более быстро действует.
Спок, средство, – это концепция, которая приносит силу 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() {}
Методы функций
Сердце все! Описывают спецификации, поведение ожидается от его применения. Методы, названные фразы говорят сами за себя и состоят из блоков с указанием этапов спецификация:
GIVEN :, где вы делаете запуска тестирования, то есть, инициализации переменных. Может быть неявной, то есть, не должны быть объявлены.
WHEN : представляет собой стимул для тестирования, один вход, внешнего действия. Всегда случается, что стимул, как мы надеемся, то ( then ) ответа.
THEN : ответ на стимул, что ожидать после блока when . По сути, эти блоки возникают вместе, и может быть более одной пары when/then метод. Таким же образом, что expect этот блок всегда это результат условий. Мы будем рассматривать условия, еще впереди.
EXPECT : вроде then , является более естественным для случаев, где это не интересно использовать блок when . Это происходит особенно в методах сугубо функциональных, то есть, без побочных эффектов. Присоединение стимул испытания в ответ на одно выражение, имеем презентации более лаконичным.
CLEANUP : – подходящее место, чтобы завершить использование ресурсов, таких как доступ к файлам, streams, etc.
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 } }
Спок позволяет использовать три категории мок:
Мок – : Переопределяет поведение в классе, когда она вызывается, и позволяет проверить, как она используется. Это самый известный и имеет акцент на определение “договор”, как он должен быть назван, что должен отвечать и как часто.
Шпион : Не переопределяет поведение, но она позволяет проверить, когда фактический метод класса вызывается, аргументы и результаты;
Заглушки : Только поведение, но и не позволяет проверить вызовы и заглушки не протестировать этот бесплатный. Черный ящик, который не хочет называться, но если это так, возвращает ответ по умолчанию.
Чтобы продолжить обучение…
Код материала : будет, что мы что-то улучшает.
Насмешки – это не заглушки : Отличный текст, чтобы дядя Боб
Спок and testing RESTful Api Как раз и тестирования без тест API RESTful?
Спок Report Extension : расширение весьма полезно, чтобы показать боссу.
Оригинал: “https://dev.to/leandrostl/crie-testes-unitarios-mais-amigaveis-e-mais-rapidamente-com-spock-4bgi”