Skip to content

Groovy Guide — Spock: Specification Testing Framework

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Groovy Guide. We cover key concepts, practical examples, and best practices to help you master this topic.

Spock is a specification-based testing framework that uses Groovy's syntax to create readable tests with given-when-then blocks, built-in mocking, data tables, and detailed failure reporting.

What You'll Learn

  • Spock specifications
  • Given-When-Then blocks
  • Data-driven testing
  • Mocking and stubbing
  • Exception testing

Why It Matters

Spock makes tests readable documentation. The given-when-then structure matches how developers think about behavior. Durga Antivirus Pro uses Spock for service layer testing.

Real-World Use

Enterprise application testing, microservice validation, and behavior-driven development.

flowchart LR
    A["Spock"] --> B["Specifications"]
    B --> C["Given-When-Then"]
    C --> D["Data Tables"]
    D --> E["Mocking"]
    A:::current --> B
    style A fill:#2563eb,stroke:#2563eb,color:#fff
    style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style E fill:#f1f5f9,stroke:#94a3b8,color:#64748b

Basic Specification

import spock.lang.Specification

class CalculatorSpec extends Specification {
    def "adding two numbers returns the sum"() {
        given: "two numbers"
        def a = 2
        def b = 3

        when: "they are added"
        def result = a + b

        then: "the result is the sum"
        result == 5
    }
}

Blocks

class MathSpec extends Specification {
    def "multiplication works correctly"() {
        given: "a calculator"
        def calc = new Calculator()

        when: "multiplying numbers"
        def result = calc.multiply(4, 3)

        then: "result is correct"
        result == 12
    }

    def "demonstrating all blocks"() {
        setup: "common setup"
        def calc = new Calculator()

        when: "an operation is performed"
        def result = calc.add(1, 2)

        then: "verify result"
        result == 3

        cleanup: "clean up resources"
        calc.close()
    }
}

Data-Driven Testing

class MathSpec extends Specification {
    def "maximum of #a and #b is #max"() {
        expect:
        Math.max(a, b) == max

        where:
        a | b || max
        1 | 2 || 2
        3 | 3 || 3
        -1| 5 || 5
        0 | 0 || 0
    }

    // Named pipes
    def "string #s has length #length"() {
        expect:
        s.length() == length

        where:
        s        | length
        "hello"  | 5
        ""       | 0
        "a"      | 1
    }
}

Mocking

class EmailServiceSpec extends Specification {
    def "sending welcome email"() {
        given:
        def emailService = Mock(EmailService)
        def userService = new UserService(emailService: emailService)

        when:
        userService.register("Alice", "alice@example.com")

        then:
        1 * emailService.sendWelcome("alice@example.com")
    }

    def "error handling when email fails"() {
        given:
        def emailService = Stub(EmailService)
        emailService.sendWelcome(_) >> { throw new EmailException("Failed") }
        def userService = new UserService(emailService: emailService)

        when:
        userService.register("Alice", "alice@example.com")

        then:
        thrown(EmailException)
    }
}

Interaction Testing

class ShoppingCartSpec extends Specification {
    def "adding items to cart"() {
        given:
        def cart = new ShoppingCart()
        def item = new Item(name: "Widget", price: 10.0)

        when:
        cart.add(item)

        then:
        cart.items.size() == 1
        cart.total == 10.0
    }

    def "checkout with insufficient stock"() {
        given:
        def inventory = Mock(InventoryService)
        inventory.checkStock(_, 5) >> false
        def cart = new ShoppingCart(inventoryService: inventory)

        when:
        cart.checkout()

        then:
        thrown(InsufficientStockException)
    }
}

Common Mistakes

1. Not using data tables

Data tables reduce boilerplate. Use them for multiple test cases with different inputs.

2. Over-mocking

Spock mocks are powerful but overuse makes tests brittle. Mock at service boundaries.

3. Ignoring then vs expect

Use then for assertions after when. Use expect for single-line without side effects.

4. Incorrect interaction counts

1 * service.method() expects exactly one call. _ * means zero or more.

5. Not cleaning up shared resources

Use setupSpec() and cleanupSpec() for shared resources, setup() and cleanup() per test.

Practice Questions

1. What is the given-when-then structure? A test structure: given (setup), when (action), then (assertion). Makes tests readable.

2. How do data tables work in Spock? Define test cases as tables in the where: block. Each row generates a separate test.

3. What is the difference between Mock and Stub? Mock verifies interactions (how many calls). Stub provides canned responses. Use Mock for behavior verification.

Challenge: Write a Spock specification with data tables, mocks, and exception testing.

FAQ

{{< faq question="Can Spock test Java classes?" >} Yes. Spock works with both Groovy and Java classes. All features are available for Java classes. {{< /faq >}}

{{< faq question="How do I run Spock tests?" >} Spock tests run with JUnit runners. Use Gradle or Maven's test task. Reports show detailed failure information. {{< /faq >}}

{{< faq question="What is @Unroll?" >} Makes data-driven tests report each row as a separate test case in reports: @Unroll def "test #a and #b"() { ... }. {{< /faq >}}

{{< faq question="Can Spock test REST APIs?" >} Yes, using Groovy's HTTP library or REST client in Spock tests for Integration Testing. {{< /faq >}}

{{< faq question="What is the difference between >> and >>> ?"

returns a fixed value. >>> returns values in sequence for successive calls. {{< /faq >}}

Mini Project

Write Spock tests for a user service:

class UserService {
    def sendEmail(email) { /* real impl */ }
    def validateEmail(email) { email.contains("@") }
    def createUser(name, email) {
        if (!validateEmail(email)) throw new IllegalArgumentException("Invalid email")
        return [id: 1, name: name, email: email]
    }
}

class UserServiceSpec extends Specification {
    def service = new UserService()

    def "creating a valid user"() {
        when:
        def user = service.createUser("Alice", "alice@example.com")

        then:
        user.name == "Alice"
        user.email == "alice@example.com"
    }

    def "rejecting invalid email"() {
        when:
        service.createUser("Bob", "invalid")

        then:
        thrown(IllegalArgumentException)
    }
}

What's Next

Now that you understand Spock, explore Gradle integration for building Groovy projects.

Topic Description Link
Groovy Gradle Gradle integration {{< ref "17-gradle-integration" >}}
Groovy MOP Meta-object protocol {{< ref "18-meta-object-protocol" >}}

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro