spock-tests
Configures and runs Spock - Groovy-based JVM testing framework with given/when/then BDD blocks, where: data tables for parametrized tests, built-in mocking via Mock()/Stub()/Spy(), interaction-based testing (verify method calls in declarative DSL), implicit assertions in then: blocks. Use when working with Java/Kotlin codebases that benefit from Groovy DSL expressiveness, or maintaining existing Spock projects.
Install with skills.sh (any agent)
npx skills add testland/qa --skill spock-testsspock-tests
Overview
Per spockframework.org/spock/docs (opens in new window):
Spock is a Groovy-based JVM test framework. Distinguishing properties:
For Kotlin-only projects, kotest-tests covers similar BDD territory natively. Spock's Groovy syntax is the strongest argument; modern JVM projects often prefer Kotlin DSLs.
Step 1 - Install
build.gradle.kts:
plugins {
id("groovy") // Groovy plugin needed for Spock
}
dependencies {
testImplementation("org.spockframework:spock-core:2.4-M5-groovy-4.0")
testImplementation(platform("org.junit:junit-bom:5.11.0"))
testImplementation("org.junit.platform:junit-platform-launcher")
}
tasks.test {
useJUnitPlatform() // Spock 2 runs on JUnit Platform
}Test files: src/test/groovy/**/*Spec.groovy.
Step 2 - First test
Per sp-docs (opens in new window) structure:
import spock.lang.Specification
class CalculatorSpec extends Specification {
def "adds two numbers"() {
given:
def calc = new Calculator()
when:
def result = calc.add(1, 2)
then:
result == 3
}
}The then: block contains assertions. Each statement is implicitly a boolean assertion - failure shows the full expression value, not just true/false.
Step 3 - given/when/then blocks
Per sp-docs (opens in new window):
| Block | Purpose |
|---|---|
setup: / given: | Test fixture setup |
when: | Action being tested |
then: | Assertions on the action's effect |
expect: | Combined when+then for simple cases |
where: | Data table for parametrized tests |
cleanup: | Per-test cleanup |
and: | Subdivider for any block |
def "user is created with default role"() {
expect:
new User("alice").role == "user"
}
def "registration validates email"() {
given:
def service = new RegistrationService()
when:
service.register(email)
then:
InvalidEmailException ex = thrown()
ex.message == "Invalid email format"
where:
email << ["", "no-at-sign", "@no-domain", "spaces in@email.com"]
}Step 4 - Data tables (Spock-distinctive)
The where: data table is Spock's killer feature:
def "addition cases"() {
expect:
Calculator.add(a, b) == result
where:
a | b || result
1 | 2 || 3
0 | 0 || 0
-1 | 1 || 0
100 | 200 || 300
}Cleaner than JUnit 5's @CsvSource for visual inspection. Each row runs as a separate test; failures don't stop subsequent rows.
Step 5 - Built-in mocking
def "user service calls repository"() {
given:
def repo = Mock(UserRepository)
def service = new UserService(repo)
when:
service.create("alice@example.com")
then:
1 * repo.save(_) // exactly 1 call to save() with any arg
}
def "service handles repo failure"() {
given:
def repo = Stub(UserRepository) {
save(_) >> { throw new SQLException("connection lost") }
}
def service = new UserService(repo)
when:
service.create("alice@example.com")
then:
thrown(SQLException)
}
def "spy delegates but observes"() {
given:
def realRepo = new InMemoryRepository()
def spy = Spy(realRepo)
def service = new UserService(spy)
when:
service.create("alice@example.com")
then:
1 * spy.save(_) >> { call -> callRealMethod() } // delegate to real impl
}Mock vs Stub vs Spy:
Step 6 - Interaction cardinality
then:
1 * service.method() // exactly once
0 * service.method() // never
(1..3) * service.method() // 1 to 3 times
(_) * service.method() // any number (default)
1 * service.method(_) // any args
1 * service.method("alice", _) // partial matchers
1 * service.method() >> 42 // returns 42 when called
1 * service.method() >>> [1, 2, 3] // returns 1 first call, 2 second, etc.Step 7 - Lifecycle hooks
def setupSpec() { /* once before all tests in spec */ }
def cleanupSpec() { /* once after */ }
def setup() { /* before each test */ }
def cleanup() { /* after each test */ }Step 8 - CI integration
Same as JUnit (Spock runs on JUnit Platform):
- run: ./gradlew test jacocoTestReport
- uses: codecov/codecov-action@v4
with: { files: build/reports/jacoco/test/jacocoTestReport.xml }Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Use Mockito alongside Spock | Two mocking APIs in one suite | Use Spock's built-in (Step 5) |
expect: for multi-step setup | Mixes given + when + then | Use given/when/then explicitly |
Skip 1 * cardinality, just _ * | Loses interaction-count check | Always specify (Step 6) |
| Use Spock for Java-only project | Groovy adds dependency + classpath weight | Use JUnit 5 (Java-native) |
Mock when Stub suffices | Over-strict; tests fail on incidental calls | Stub for non-verified deps (Step 5) |
Limitations
References
Related skills
assertj
Reference for AssertJ - the canonical JVM fluent-assertion library pairable with JUnit 5 / TestNG / Spock; covers the assertThat() entry point, collection matchers (contains, containsExactly, allSatisfy, extracting), exception assertions (assertThatThrownBy, catchThrowable), SoftAssertions for multi-failure collection, recursive comparison (usingRecursiveComparison) for deep equality, and domain-specific custom assertions via AbstractAssert. Use when writing JVM tests that need richer failure messages than built-in assertEquals, or when verifying complex object graphs, exception types, or collections.
junit5-tests
Configures and runs JUnit 5 (Jupiter) - modern JVM testing platform with annotations (`@Test` / `@ParameterizedTest` / `@RepeatedTest` / `@TestFactory`), lifecycle hooks (`@BeforeAll` / `@BeforeEach` / `@AfterEach` / `@AfterAll`), extension model (`@ExtendWith`), display names (`@DisplayName`), conditional execution (`@EnabledOnOs`, `@EnabledIf`), parallel execution config; integrates with Maven Surefire / Gradle test task / IntelliJ. Use when the user works with Java / Kotlin codebases needing the modern JVM standard.
kotest-tests
Configures and runs Kotest - Kotlin-native test framework with multiple specification styles (StringSpec, FunSpec, BehaviorSpec, DescribeSpec, ShouldSpec, FreeSpec, FeatureSpec, ExpectSpec, AnnotationSpec); rich matcher library; built-in property-based testing (alternative to jqwik); coroutines support; data-driven testing; isolation modes per-spec or per-test; integrates with Gradle JVM test task. Use when working with Kotlin and wanting Kotlin-idiomatic DSL over JUnit 5's annotation-driven approach. Matchers (shouldBe, shouldContain) are bundled with the runner and are not a drop-in replacement for a standalone JVM assertion library; for assertion-only use paired with JUnit 5 / TestNG / Spock see assertj.
scalatest
Configures and runs ScalaTest - Scala-native test framework with multiple specification styles (FlatSpec, FunSuite, WordSpec, FreeSpec, AsyncFlatSpec for async); Matchers DSL (`should equal`, `should contain`, `shouldBe a [Class]`); integrates with ScalaCheck for property-based testing; sbt + Maven + Gradle support; tagged-test selective execution. Use when working with Scala codebases.
testng-tests
Configures and runs TestNG - JVM testing framework with `@Test` priorities + groups + `dependsOnMethods`; `@DataProvider` for parametrized tests with method-level data sources; `testng.xml` suite definitions for grouping + parallelism config; listeners (`ITestListener`, `ISuiteListener`) for hooks; `ITestContext` for cross-test state; integrates with Maven Surefire / Gradle. Use when working with legacy TestNG codebases or needing TestNG-specific features (test method dependencies, suite-level XML config).