Spock что это за программа на Андроид

Представляем Spock, среду тестирования и спецификации для JVM

Spock — это новая среда тестирования и спецификации для разработчиков на Java и Groovy. Когда я распространял информацию о Споке, у меня часто возникал вопрос: «С какой стати мне нужен еще один фреймворк для тестирования? Разве уже не достаточно клонов xUnit / xSpec?» В этом посте я постараюсь показать вам, чем отличается Спок, и почему это стоит поближе.

Утверждения

Утверждения являются наиболее фундаментальным способом выражения поведения части программного обеспечения. Это почти традиция, что каждая новая среда тестирования поставляется с собственным API подтверждения, добавляя несколько складок тут и там, и называя вещи немного по-другому. Например, чтобы сказать, что две вещи равны, вы обычно пишете что-то вроде:

  • assertEquals (x, y)
  • x.shouldEqual (у)
  • x.mustBe (у)

Время от времени появляется незнакомец и спрашивает, не может ли он просто использовать простые логические выражения вместо API утверждений. Но мы все знаем, что логические выражения не могут сказать нам, почему они потерпели неудачу. Или они могут? Знакомьтесь, Спок!

@Speck
class Assertions def "comparing x and y"() def x = 1
def y = 2

expect:
x < y // OK
x == y // BOOM!
>
>

В Споке утверждения следуют за ожидаемым: label, и это именно то, что просил незнакомец — простые логические выражения! Вот что мы получаем, когда запускаем этот тест:

Condition not satisfied:

x == y
| | |
1 | 2
false

Конечно, это работает не только для простых сравнений, но и для произвольных утверждений. Вот еще один, немного более интересный вывод:

Condition not satisfied:

Math.max(a, b) > c
| | | | |
112 | 94 | 115
112 false

Опять же, среда выполнения Спока собрала всю необходимую информацию и представила ее интуитивно понятным способом. Хорошо, не правда ли?

Управляемые данными тесты

Хотя управляемое данными тестирование является естественным продолжением тестирования на основе состояния, оно, похоже, используется не очень часто. Я полагаю, это потому, что тестовые среды делают написание тестов, управляемых данными, довольно сложными. Все они? Знакомьтесь, Спок!

@Speck
class DataDriven def "maximum of two numbers"() expect:
Math.max(a, b) == c

where:
a b c >
>

Блок ожидания предоставляет только тестовую логику, используя свободные (т.е. неопределенные) переменные a, b и c в качестве заполнителей для данных. Затем до блока where можно связать конкретные значения с этими переменными. Когда этот тест выполняется, он повторяется три раза: сначала с a = 7, b = 3 и c = 7; тогда с а = 4, б = 5 и с = 5; и наконец с a = b = c = 9.

Конечно, данные теста не должны быть включены в тест. Например, давайте попробуем загрузить его из базы данных:

@Speck
class DataDriven @Shared sql = Sql.newInstance("jdbc:derby:spockdata", "org.apache.derby.jdbc.EmbeddedDriver")

def "maximum of two numbers"() expect:
Math.max(a, b) == c

where:
row a = row["a"]
b = row["b"]
c = row["c"]
>

Здесь значения a, b и c берутся из столбцов с одинаковыми именами в таблице maxdata, пока не останется больше строк. Используя синтаксис, похожий на множественное назначение Groovy, блок where может быть еще более упрощен:

where:
[a, b, c]

Вместе с созданием экземпляра Sql, описанного выше, теперь у нас осталось две строки кода для загрузки наших тестовых данных из базы данных. Насколько это легко?

Тестирование в Kotlin при помощи Spock

Цель статьи заключается в том, чтобы показать какие возникают трудности при использовании Spock с Kotlin, какие есть пути их разрешения и ответить на вопрос, стоит ли использовать Spock, если вы разрабатываете на Kotlin. Подробности под катом.

Я работаю в компании, в которой практикуется экстремальное программирование. Одним из основных приёмов экстремального программирования, который мы используем в повседневной работе, является TDD (test-driven development). Это означает, что прежде чем изменять код, мы пишем тест, который покрывает желаемое изменение. Таким образом, мы регулярно пишем тесты и имеем покрытие кода тестами близкое к 100%. Это предъявляет определенные требования к выбору тестового фрэймворка: одно дело — писать тесты раз в неделю, совсем другое — делать это каждый день.

Мы занимаемся разработкой на Kotlin и в определенный момент в качестве основного фрэймворка мы выбрали Spock. С того момента прошло около 6 месяцев, чувство эйфории и новизны прошло, поэтому статья является своего рода ретроспективой, в которой я попытаюсь рассказать, с какими трудностями мы столкнулись за это время и как мы их разрешали.

Почему именно Spock?

В первую очередь нужно разобраться, какие фрэймворки позволяют тестировать Kotlin и какие преимущества дает именно Spock по сравнению с ними.

Одним из достоинств Kotlin является его совместимость с Java, что позволяет использовать для тестирования любые Java фрэймворки, такие как Junit, TestNG, Spock и т.д. В то же время есть фрэймворки разработанные специально для Kotlin такие как Spek и Kotest. Почему мы выбрали имеено Spock?

Я бы выделил следующие достоинства:

  • Во-первых, Spock написан на Groovy. Хорошо это или плохо — судите сами. Про Groovy можно почитать статейку вот тут. Лично меня в Groovy привлекает лаконичный синтаксис, наличие динамической типизации, встроенный синтаксис для списков, обычных и ассоциативных массивов, а также совершенно сумасшедшие преобразования типов.
  • Во-вторых, Spock уже содержит mock-фрэймворк (MockingApi) и assertion-библиотеку;
  • Еще Spock отлично подходит для написания параметризованных тестов:
def "maximum of two numbers"()

Это очень поверхностное сравнение (если это вообще можно назвать сравнением), но это примерно те причины, по которым мы выбрали Spock. Естественно, в последующие недели и месяцы мы столкнулись с некоторыми трудностями.

Проблемы, возникающие при тестировании Kotlin при помощи Spock и их решения

Тут я сразу оговорюсь, что проблемы, которые описаны ниже, не являются уникальными для Spock, они будут актуальны для любого Java-фрэймворка и связаны они, в основном, с совместимостью с Kotlin.

1. final по умолчанию

В отличие от Java, все классы Kotlin по умолчанию имеют модификатор final , что предотвращает дальнейшее создание потомков и переопределение методов. Проблема тут заключается в том, что при создании mock-объектов какого-либо класса, большинство mock-фрэймворков пытаются создать proxy-объект, который как раз и является потомком исходного класса и переопредяет его методы.

Поэтому если у вас есть сервис:

class CustomerService < fun getCustomer(id: String): Customer < // Some logic >> 

и вы попытаетесь создать mock этого сервиса в Groovy:

def customerServiceMock = Mock(CustomerService) 

, то вы получите ошибку:

org.spockframework.mock.CannotCreateMockException: Cannot create mock for class CustomerService because Java mocks cannot mock final classes 
  • Первым, что приходит на ум, является использование ключевого слова open , которое позволит другим классам наследоваться от данного. Недостатками такого решение будет очевидное «загрязнение» кода, а также предоставление возможности расширять класс, который, возможно, вовсе не предназначен для расширения;
  • Вторым решением будет использование all-open плагина для компилятора. Этот плагин делает классы и их члены открытыми на этапе компиляции. Этот плагин необходим в том числе для использования Spring Framework и Hibernate, потому что они полагаются на использование cglib(Code Generation Library);
  • Также, можно использовать одну из mock-библиотек, разработанных специально для Kotlin (например, mockk). Однако, этот метод нельзя назвать предпочтительным, так как Spock уже содержит свою собственную библиотеку Spock MockingApi.

2. Значения аргументов по умолчанию

В Kotlin аргументы функции или конструктора могут иметь значения по умолчанию, которые используются в том случае, если аргумент функции не указан при её вызове. Трудность заключается в том, что байт-код Java теряет значения аргументов по умолчанию и имена параметров функции и поэтому при вызове функции или конструктора Kotlin из Spock требуется явно указывать все значения.

Рассмотрим класс Customer , который имеет конструктор с 2-мя обязательными полями email и name и опциональными полями, имеющими значения по умолчанию:

data class Customer( val email: String, val name: String, val surname: String = "", val age: Int = 18, val identifiers: List = emptyList(), val addresses: List = emptyList(), val paymentInfo: PaymentInfo? = null ) 

Для того, чтобы создать экземпляр этого класса в Groovy Spock тесте, придется передать в конструктор значения для всех аргументов:

new Customer("[email protected]", "John", "", 18, [], [], null) 
  • Первое решение проблемы — использование аннотации @JvmOverloads , которая создаст перегруженные конструкторы или перегруженные функции.
  • Также, можно использовать отдельный groovy класс — фабрику объектов, чтобы делегировать логику по созданию и популяции объектов:
class ModelFactory < static def getCustomer() < new Customer( "[email protected]", "John", "Doe", 18, [nationalIdentifier], [address], paymentInfo ) >static def getAddress() < new Address(/* arguments */) >static def getNationalIdentifier() < new NationalIdentifier(/* arguments */) >static def getPaymentInfo() < new PaymentInfo(/* arguments */) >> 

3. Java Reflection vs Kotlin Reflection

Это может быть довольно узкой областью, но если вы планируете использовать рефлексию в своих тестах, то стоит особенно хорошо подумать, стоит ли использовать Groovy. В нашем проекте мы используем много аннотаций и, чтобы не забыть аннотировать определенные классы или определенные поля аннотациями, мы пишем тесты с использованием рефлексии. Тут-то и возникают трудности.

Классы и аннотации написаны на Kotlin, логика, связанная с тестами и рефлексией на Groovy. Из-за этого в тестах получается мешанина из Java Reflection API и Kotlin Reflection:

Конвертация Java классов в Kotlin:

def kotlinClass = new KClassImpl(clazz) 

Вызов Kotlin extension-функций как статических методов:

ReflectJvmMapping.getJavaType(property.returnType) 

Трудночитаемый код, где будут использоваться типы Kotlin:

private static boolean isOfCollectionType(KProperty1 property) < // Some logic >

Конечно, это не является чем-то непосильным или невозможным. Вопрос только в том, а не проще ли это сделать при помощи Kotlin фрэймворка для тестирования, где можно будет использовать extension-функции и чистый Kotlin Reflection??

4. Coroutines

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

Если создать suspend функцию:

suspend fun someSuspendFun() < // Some logic >

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

public void someSuspendFun(Continuation $completion) < // Some logic >

, где Continuation — это Java-класс, который инкапсулирует логику по выполнению корутины. Вот тут и возникает проблема, как создать объект этого класса? Также, в Groovy недоступен runBlocking. И поэтому вообще не очень ясно, как тестировать код с корутинами в Spock.

Итоги

За полгода использования Spock он принес нам больше хорошего, чем плохого и о выборе мы не жалеем: тесты легко читать и поддерживать, писать параметризованные тесты — одно удовольствие. Ложкой дегтя в нашем случае оказалась рефлексия, но она используется всего в нескольких местах, то же самое с корутинами.

При выборе фрэймворка для тестирования проекта, который разрабатывается на Kotlin стоит хорошо подумать, какие функции языка вы планируете использовать. В случае если вы пишете простое CRUD-приложение, Spock будет идеальным решением. Используете корутины и рефлексию? Тогда лучше посмотрите на Spek или Kotest.

Материал не претендует на полноту, поэтому если вы сталкивались с другими трудностями при использовании Spock и Kotlin — пишите в комментариях.

Полезные ссылки

  • Документация Spock
  • Документация Groovy

spock учебник
Начало работы с spock

Spock - это платформа тестирования и спецификации для приложений Java и Groovy. Он использует язык выраженных спецификаций.

Какой Самый Просто�.

Please enable JavaScript

Разработчик, как правило, разработчик Java, использующий groovy , может использовать Spock в качестве основы для проверки (тестирования). Тестер может использовать Spock для написания функциональных проверок программного пакета, обеспечивающего полную мощность groovy .

Это в основном позволяет писать спецификации, описывающие ожидаемые функции.

Версии

Версия Дата выхода
1.1 rc 3 2016-11-03
1,0 2016-03-02

Установка или настройка

Информацию о структуре Spock можно найти на веб-сайте Spock .

Существует три способа использования Spock в Groovy

  1. как зависимость, использующая менеджер зависимостей Grape:

Добавьте следующий код к вашему оригинальному скрипту.

@Grab(group='org.spockframework', module='spock-core', version='1.1-groovy-2.4.1') 
@Grab('org.spockframework:spock-core:1.1-groovy-2.4.1') 
  1. как зависимость maven с помощью инструмента построения Gradle (build.gradle)

Добавьте следующую зависимость в файл build.gradle в зависимости от зависимостей

. dependencies < // mandatory dependencies for using Spock compile "org.codehaus.groovy:groovy-all:2.4.1" testCompile "org.spockframework:spock-core:1.0-groovy-2.4" >. 
  1. Добавление библиотеки spock-core в ваш путь к lib

Добавление spock-core-1.0-groovy-2.4.jar в место в вашем пути к классу, где groovy может его найти.

и последнее, но не менее важное, вам нужно импортировать библиотеку, чтобы ее можно было использовать в своем оригинальном скрипте

import spock.lang.* 

После того, как вы установили spock, попробуйте один из примеров hello world.

«Привет мир», используя, когда и затем или ожидаем

import spock.lang.* class HelloWorldSpec extends Specification < @Shared message = 'Hello world!' def "The world can say hello using when and then"() < when: def newMessage = message then: newMessage == 'Hello world!' >def "The world can say hello using expect"() < expect: message == 'Hello world!' >> 

При подготовке материала использовались источники:
https://coderlessons.com/articles/java/predstavliaem-spock-sredu-testirovaniia-i-spetsifikatsii-dlia-jvm
https://habr.com/ru/post/489562/?mobile=no
https://learntutorials.net/ru/spock/topic/5431/%D0%BD%D0%B0%D1%87%D0%B0%D0%BB%D0%BE-%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B-%D1%81-spock

Оцените статью