Groovy Guide — Spock: Specification Testing Framework
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