Spock — это среда тестирования и спецификации для приложений Java и Groovy. Спок это:
- Чрезвычайно выразительный
- Облегчает заданный / Когда / Тогда синтаксис для ваших тестов
- совместим с большинством IDE и CI-серверов.
Звучит интересно? Что ж, вы можете начать играть в Spock очень быстро, посетив веб-консоль Spock . Если у вас есть небольшой тест, который вам нравится, вы можете опубликовать его, как я сделал для этого маленького теста Hello World .
Во-первых, тесты Спока написаны на Groovy. Это означает, что некоторый код, который у вас есть с Java, исчезнет. Этот тест Hello World служит мягким введением в некоторые функции Spock.
- Не нужно указывать, что класс является Public, поскольку это по умолчанию.
- Нет необходимости объявлять firstWord и lastWord как строки
- Нет необходимости явно вызывать assert, поскольку каждая строка кода в блоке ожидаемого получает это автоматически. Просто убедитесь, что строки в этом блоке являются логическим выражением. Так что в этом случае это просто выражение равенства, которое будет либо истинным, либо ложным.
Так меньше кода котельной плиты, что дальше? Ну, вы знаете эти действительно длинные имена тестов, которые вы получаете с помощью тестов JUnit, и вместо того, чтобы вызывать этот тест helloWorldIntroductionToSpockTest (), который трудно читать, вы можете просто использовать строку с пробелами для названия теста: Hello World введение в Spock тест Это делает вещи намного более читабельными.
В-третьих, если бы я внес небольшое изменение в тест и изменил firstWord на « Hello1 », тест, конечно, провалился. Но когда я получаю ошибку в Споке, я получаю полный контекст проверенного выражения. Я вижу значение firstWord , значение secondWord и значение после конкатенации, что позволяет намного быстрее диагностировать проблемы при сбое тестов.
Насмешка и Стаббинг Не плохо для вступления. Давайте теперь посмотрим на больше возможностей.
В JUnit гораздо мощнее насмешки и заглушки ( и различные дополнения ). Но это не только супер мощный в Споке, но и очень краткий, сохраняя ваш тестовый код очень аккуратным и легко читаемым.
Предположим, что мы хотим заглушить класс с именем PaymentCalculator в нашем тесте, точнее метод, Calculate (Product product, Integer count). В версии с заглушкой мы хотим вернуть количество, умноженное на 10, независимо от стоимости продукта. В Споке мы достигаем этого путем:
1
2
|
<span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >PaymentCalculator paymentCalculator = Stub(PaymentCalculator)</span> PaymentCalculator paymentCalculator = Stub (PaymentCalculator)</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >paymentCalculator.calculate(_, _) >> {p, c -> c * 10 }</span> paymentCalculator.calculate (_, _) >> {p, c -> c * 10 }</span> |
Если вы еще не поняли, насколько это коротко и аккуратно, тогда принесите себе кофе . Если вы хорошо поняли, у вас все еще может быть сундук, но учтите следующие моменты:
- Подчеркивание в среднем для всех значений
- На правой стороне мы видим Groovy Closure. А пока, представьте, что это анонимный метод с двумя входами. р для продукта, с для подсчета. Мы не должны их печатать. Это просто больше кода котельной плиты больше нет.
- Закрытие всегда возвращает время отсчета 10. Нам не нужно возвращать оператор. Значение последнего выражения всегда возвращается. Опять же, это означает меньше кода котельной плиты. Когда заглушка становится такой простой и аккуратной, это означает, что вы действительно можете сосредоточиться на тесте — круто.
Параметризованные тесты
Лучший способ объяснить это на примере.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
<span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > @Unroll </span> @Unroll </span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >def "Check that the rugby player #player who has Irish status #isIrish plays for Ireland" (String player, Boolean isIrish) {</span> def "Убедитесь, что игрок в регби #player, имеющий ирландский статус #isIrish, играет за Ирландию" (String player, Boolean isIrish) {</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >given: "An instance of Rugby player validator" </span> дано: «Экземпляр валидатора игрока в регби»</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >RugbyPlayerValidator rugbyPlayerValidator = new RugbyPlayerValidator()</span> RugbyPlayerValidator rugbyPlayerValidator = new RugbyPlayerValidator ()</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >expect:</span> ожидать:</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >rugbyPlayerValidator.isIrish(player) == isIrish</span> rugbyPlayerValidator.isIrish (player) == isIrish</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >where:</span> где:</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >player ||</span> игрок ||</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >isIrish</span> isIrish</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > "Johny Sexton" ||</span> "Джонни Секстон" ||</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > true </span> правда</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > "Stuart Hogg" ||</span> "Стюарт Хогг" ||</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > false </span> ложный</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > "Conor Murray" ||</span> "Конор Мюррей" ||</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > true </span> правда</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > "George North" ||</span> "Джордж Норт" ||</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > false </span> ложный</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > "Jack Nowell" ||</span> "Джек Новелл" ||</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > true </span> правда</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >}</span> }</span> |
В этом параметризованном тесте мы видим следующее:
- Тест параметризован, мы это в сигнатуре теста и в блоке where .
- Существует один входной параметр player и один выходной параметр — который соответствует ожидаемому значению.
- Тест параметризован пять раз. Входные параметры слева, вывод справа. Конечно, можно получить и то и другое, в этом тесте у нас есть только один из них.
- Аннотация @Unroll будет означать, что если тест не пройден, будут выведены значения всех параметров. Сообщение заменит данные игрока в #player, а сведения об ирландском статусе будут заменены на #isIrish. Так, например, « Проверяет, что игрок в регби Джек Новелл, имеющий истинный ирландский статус, играет за Ирландию »
Опять же, это позволяет гораздо быстрее сузить ошибки. Тест неправильный или код неправильный? Это становится вопросом, на который можно ответить быстрее. В этом случае тест неверен.
Все преимущества Groovy
Что еще? Ну, еще одно важное преимущество — все преимущества Groovy. Например, предположим, что вы тестируете API, который возвращает JSON или XML. Groovy отлично подходит для анализа XML и JSON. Предположим, у нас есть API, который возвращает информацию о спортивных игроках в формате XML. Формат варьируется, но незначительно, в зависимости от вида спорта, в котором они играют:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
< span class = "notranslate" onmouseover = "_tipon(this)" onmouseout = "_tipoff()" >< span class = "google-src-text" style = "direction: ltr; text-align: left" >Joey Carberry</ span > Джои Карберри</ span > < span class = "notranslate" onmouseover = "_tipon(this)" onmouseout = "_tipoff()" >< span class = "google-src-text" style = "direction: ltr; text-align: left" >< details ></ span > <подробно></ span > < span class = "notranslate" onmouseover = "_tipon(this)" onmouseout = "_tipoff()" >< span class = "google-src-text" style = "direction: ltr; text-align: left" >< rugbysummarycategory ></ span > < Rugbysummarycategory ></ span > < span class = "notranslate" onmouseover = "_tipon(this)" onmouseout = "_tipoff()" >< span class = "google-src-text" style = "direction: ltr; text-align: left" >< players ></ span > <игроков></ span > < span class = "notranslate" onmouseover = "_tipon(this)" onmouseout = "_tipoff()" >< span class = "google-src-text" style = "direction: ltr; text-align: left" >< player >Joey Carberry</ player ></ span > < player > Джои Карберри </ player ></ span > < span class = "notranslate" onmouseover = "_tipon(this)" onmouseout = "_tipoff()" >< span class = "google-src-text" style = "direction: ltr; text-align: left" >< player >Teddy Thomas</ player ></ span > < player > Тедди Томас </ player ></ span > < span class = "notranslate" onmouseover = "_tipon(this)" onmouseout = "_tipoff()" >< span class = "google-src-text" style = "direction: ltr; text-align: left" ></ players ></ span > </ Проигрыватели></ span > < span class = "notranslate" onmouseover = "_tipon(this)" onmouseout = "_tipoff()" >< span class = "google-src-text" style = "direction: ltr; text-align: left" ></ rugbysummarycategory ></ span > </ Rugbysummarycategory ></ span > < span class = "notranslate" onmouseover = "_tipon(this)" onmouseout = "_tipoff()" >< span class = "google-src-text" style = "direction: ltr; text-align: left" ></ details ></ span > </ Детали></ span > < span class = "notranslate" onmouseover = "_tipon(this)" onmouseout = "_tipoff()" >< span class = "google-src-text" style = "direction: ltr; text-align: left" >< details ></ span > <подробно></ span > < span class = "notranslate" onmouseover = "_tipon(this)" onmouseout = "_tipoff()" >< span class = "google-src-text" style = "direction: ltr; text-align: left" >< footballsummarycategory ></ span > < Footballsummarycategory ></ span > < span class = "notranslate" onmouseover = "_tipon(this)" onmouseout = "_tipoff()" >< span class = "google-src-text" style = "direction: ltr; text-align: left" >< players ></ span > <игроков></ span > < span class = "notranslate" onmouseover = "_tipon(this)" onmouseout = "_tipoff()" >< span class = "google-src-text" style = "direction: ltr; text-align: left" >< player >Lionel Messi</ player ></ span > < player > Лионель Месси </ player ></ span > < span class = "notranslate" onmouseover = "_tipon(this)" onmouseout = "_tipoff()" >< span class = "google-src-text" style = "direction: ltr; text-align: left" >< player >Cristiano Ronaldo</ player ></ span > < player > Криштиану Роналду </ player ></ span > < span class = "notranslate" onmouseover = "_tipon(this)" onmouseout = "_tipoff()" >< span class = "google-src-text" style = "direction: ltr; text-align: left" ></ players ></ span > </ Проигрыватели></ span > < span class = "notranslate" onmouseover = "_tipon(this)" onmouseout = "_tipoff()" >< span class = "google-src-text" style = "direction: ltr; text-align: left" ></ footballsummarycategory ></ span > </ Footballsummarycategory ></ span > < span class = "notranslate" onmouseover = "_tipon(this)" onmouseout = "_tipoff()" >< span class = "google-src-text" style = "direction: ltr; text-align: left" ></ details ></ span > </ Детали></ span > |
Мы хотим просто вызвать этот API, а затем проанализировать игроков независимо от вида спорта. Мы можем разобрать это полиморфно очень просто в Groovy.
1
2
|
<span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >def rootNode = new XmlSlurper().parseText(xml)</span> def rootNode = new XmlSlurper (). parseText (xml)</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >List players = rootNode. '*' .Players.Player*.text()</span> Список игроков = rootNode. '*' . Players.Player * .text ()</span> |
Некоторые ключевые моменты:
- Сила динамической типизации незамедлительна. Выражение может быть динамически вызвано на rootNode. Не нужно многословного, сложного выражения XPath.
- «*», Как подстановочный знак. Это будет охватывать как RugbySummaryCategory, так и FootballSummaryCategory.
- Player *, означает для всех элементов Player. Так что никакой глупой многословности для цикла здесь не нужно
- Выражение text () просто вытягивает значения текста между соответствующими элементами Player. Так почему же теперь есть список всех игроков, которые можно просто сделать: Players.size () == 4 Помните, что нет необходимости утверждать.
Предположим, мы хотим проверить имена игроков. В данном случае нам не важен порядок, поэтому имеет смысл преобразовать список в набор и затем проверить. Просто.
1
|
<span class= "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class= "google-src-text" style= "direction: ltr; text-align: left" >players as Set = [ "Joey Carberry" , "Teddy Thomas" , "Lionel Messi" , Cristiano Ranaldo "] as Set</span> игроки как Сет = [" Джои Карберри ", " Тедди Томас ", " Лионель Месси ", Криштиану Ранальдо " ] как Сет< /span > |
Это преобразует оба списка в набор, что означает, что проверка порядка исчезла, и это просто сравнение набора. Есть еще масса преимуществ Groovy, которыми мы можем воспользоваться. Но красота в том, что мы на самом деле не должны.
Весь код Java также допустим в классе Groovy . То же самое относится и к Споку. Это означает, что для тех, кто имеет опыт работы с Java, крутой кривой обучения нет. Они могут кодировать чистую Java, а затем получать советы по Groovy из обзоров кода и т. Д.
Мощные аннотации
Спок также имеет ряд мощных аннотаций для ваших тестов. Опять же, мы видим силу Groovy здесь, поскольку мы можем закрыть эти аннотации. Например:
1
2
|
<span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > @IgnoreIf ({System.getProperty( "os.name" ).contains( "windows" )})</span> @IgnoreIf ({System.getProperty ( "os.name" ). Содержит ( "окно" )})</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >def "I'll run anywhere except windows" () {...}</span> def "Я буду бегать где угодно, кроме Windows" () {...}</span> |
Или просто сделайте ваш тест неудачным, если они будут выполняться слишком долго
1
2
|
<span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > @Timeout (value = 100 , unit=TimeUnit.MILLISECONDS)</span> @Timeout (значение = 100 , единица измерения = TimeUnit.MILLISECONDS)</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >def "I better be quick" () {...}</span> def "Мне лучше быть быстрым" () {...}</span> |
Итак, в целом, Spock против ванили JUnit имеет следующие преимущества:
- Тестовая структура обязательна. Нет больше случайных утверждений. Утверждения могут быть только в обозначенных частях кода.
- Тестовый код гораздо удобнее для чтения.
- Намного больше информации о контексте проваленного теста
- Может издеваться и заглушки с гораздо меньшим количеством кода
- Может использовать кучу возможностей Groovy, чтобы сделать код гораздо менее многословным
- Очень мощная тестовая параметризация, которая может быть выполнена очень аккуратно
- Ряд мощных аннотаций.
И одним из часто забываемых моментов является то, что ваш проект не должен быть написан на Groovy. Вы можете сохранить все это в Java и использовать статическую типизацию Java для своего производственного кода, а также использовать мощь и скорость Groovy для своего тестового кода.
До следующего раза береги себя.
Опубликовано на Java Code Geeks с разрешения Алекса Стейвли, партнера нашей программы JCG . Смотрите оригинальную статью здесь: Тестирование вашего кода с помощью Спока
Мнения, высказанные участниками Java Code Geeks, являются их собственными. |