Перейти к основному содержимому

Spock — первая спецификация

Разработчику

Spock — первая спецификация

Spock — фреймворк тестов на Groovy, который хорошо стыкуется с Java-проектами: production-код на Java, тесты — в src/test/groovy. Spock компилируется в байт-код JVM и запускается через JUnit Platform (та же инфраструктура, что у JUnit 5).

Главная идея — спецификация читается как текст: блоки given (данные), when (действие), then (проверка), таблица where для многих входов.

База Groovy: первая программа · теория: справочник § Spock · Java-тесты: JUnit 5 · сборка: Gradle Groovy DSL.


Что получится

Проект с Java-классом Calc, Spock-спецификацией CalcSpec, запуск ./gradlew test и понимание каждого блока теста.


Термины

ТерминПростыми словами
SpecificationКласс теста в Spock (наследник spock.lang.Specification)
Feature methodМетод def "описание на русском"() — один сценарий
Блок givenПодготовка: переменные, моки, фикстуры
Блок whenДействие: вызов тестируемого кода
Блок thenПроверки и ожидание исключений
Блок expectДействие и проверка в одном шаге
Блок whereТаблица параметров (как Excel: столбцы = переменные)
@UnrollКаждая строка where — отдельный тест в отчёте

Зачем Spock, если есть JUnit

JUnit 5Spock
@Test void shouldAdd()Имя метода = человекочитаемое предложение
@ParameterizedTest + источникиТаблица where: внизу метода
Mockito отдельноMock(), Stub() встроены в язык Spock
AssertJ / Hamcrest==, thrown(), матчеры в then

Spock не заменяет JUnit в экосистеме: он использует JUnit Platform как раннер. В CI остаётся привычный шаг test и XML-отчёты.


Что тестируем (Java)

Spock вызывает обычные Java-классы без обёрток:

// src/main/java/com/example/Calc.java
package com.example;

public class Calc {
public int add(int a, int b) { return a + b; }
public int divide(int a, int b) {
if (b == 0) throw new ArithmeticException("zero");
return a / b;
}
}

Пакет в тесте Groovy должен совпадать: package com.example.


Gradle (Groovy DSL)

В build.gradle:

plugins {
id 'java'
}

repositories {
mavenCentral()
}

dependencies {
testImplementation 'org.spockframework:spock-core:2.3-groovy-4.0'
testImplementation 'org.apache.groovy:groovy:4.0.21'
}

test {
useJUnitPlatform()
}
ЗависимостьЗачем
spock-coreSpock + интеграция с JUnit Platform
groovyКомпилятор Groovy для тестовых исходников
useJUnitPlatform()Gradle запускает тесты через JUnit 5 / Platform

Папка: src/test/groovy (зеркало пакета com/example/CalcSpec.groovy).

Подробнее про блоки Gradle: 23.md.


Первая спецификация

package com.example

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

class CalcSpec extends Specification {

def calc = new Calc()

def "сложение двух чисел"() {
given:
def a = 2
def b = 3

when:
def result = calc.add(a, b)

then:
result == 5
}

@Unroll
def "деление #a / #b = #expected"() {
expect:
calc.divide(a, b) == expected

where:
a | b | expected
10| 2 | 5
9 | 3 | 3
}

def "деление на ноль бросает исключение"() {
when:
calc.divide(1, 0)

then:
thrown(ArithmeticException)
}
}

Разбор по блокам

Класс и поле:

  • extends Specification — подключает DSL Spock (блоки, моки, setup/cleanup).
  • def calc = new Calc() — один экземпляр на спецификацию (можно вынести в setup()).

Сценарий «сложение»:

БлокРоль
given:Задаём a и b
when:Вызываем calc.add
then:Утверждение result == 5 (при ложном — провал с diff)

Параметризованный тест:

  • @Unroll — в IDE/CI будет «деление 10 / 2 = 5», «деление 9 / 3 = 3», а не одна строка.
  • expect: — когда нет отдельного «действия», только выражение-истина.
  • where: — таблица: столбцы a, b, expected разделены |.
  • В имени метода #a, #b — подстановка значений из строки where.

Исключение:

  • В when вызываем опасный код.
  • В then thrown(ArithmeticException) — тест зелёный только если исключение именно этого типа вылетело.

Запуск

./gradlew test

Windows: gradlew.bat test.

В IntelliJ IDEA: правый клик на CalcSpec → Run. Отчёт JUnit-совместимый — Jenkins/GitHub Actions подхватят без отдельной настройки Spock.

Успешный прогон в консоли: BUILD SUCCESSFUL, в build/reports/tests/test/index.html — HTML-отчёт.


Моки (кратко)

Зависимость подменяют без Mockito:

def repo = Mock(UserRepository)
repo.findById(1) >> new User(name: "Ann")

when:
def user = service.load(1)

then:
1 * repo.findById(1)
user.name == "Ann"
КонструкцияСмысл
Mock(Class)Прокси: вызовы записываются
>> значениеStub: при вызове вернуть значение
1 * repo.method(...)Проверка: метод вызван ровно один раз

Подробнее — справочник.


Частые ошибки

СимптомПричина
Spec не находитсяФайл не в src/test/groovy
No tests foundНет useJUnitPlatform() в test { }
== для объектов «падает»У POJO нет корректного equals — используйте is() / поля
Groovy не компилируетсяНесовместимые версии spock-core и groovy (смотрите матрицу на spockframework.org)

Что попробовать

  1. Добавить строку в where с ожидаемым ArithmeticException для b == 0.
  2. Spock + @SpringBootTest в Spring-модуле.
  3. Тот же ./gradlew test в Jenkins.

Дальше

Gradle Groovy DSL · Jenkins Pipeline · делегирование · о разделе


См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).