Testing Grails with Spock

Harry · 13 Sep 2026 · 2 views

Why Testing Matters

Tests make refactoring safe, keep applications stable, and define expected behavior. Grails ships with Spock, a testing framework whose given/when/then style is as readable as documentation.

Spock Basics

class SampleSpec extends Specification {

    def "should do something"() {
        given:
        // setup

        when:
        // action

        then:
        // assertions
    }
}

Unit Testing Domain Classes

class UserSpec extends Specification {

    void "username cannot be blank"() {
        when:
        def user = new User(username: "")

        then:
        !user.validate()
    }
}

Pure domain tests need no database.

Unit Testing Services

class UserServiceSpec extends Specification {
    UserService userService

    void "user creation works"() {
        when:
        userService.createUser([username: "admin"])

        then:
        User.count() == 1
    }
}

Mocking and Stubbing

Mocks isolate logic and keep external services out of tests:

def emailService = Mock(EmailService)
emailService.send(_, _) >> true

1 * emailService.send(_, _)   // verify a call happened

// stub a failure and assert the app handles it
paymentService.charge(_) >> { throw new RuntimeException("Failed") }

Data-Driven Tests

void "age validation"(int age, boolean valid) {
    expect:
    new User(age: age).validate() == valid

    where:
    age | valid
    10  | false
    18  | true
}

Integration Testing

@Integration
@Rollback
class UserIntegrationSpec extends Specification {

    void "save persists to database"() {
        when:
        new User(username: "test").save()

        then:
        User.count() == 1
    }
}

Integration tests load the full Grails context and @Rollback keeps the database clean.

Testing Controllers

class UserControllerSpec extends Specification {
    def controller = new UserController()
    def userService = Mock(UserService)

    def setup() { controller.userService = userService }

    void "index returns users"() {
        when:
        controller.index()
        then:
        response.text == "User Home"
    }
}

Test Best Practices

  • Write unit tests first; add integration tests for critical paths.
  • Mock external services - never call real ones in tests.
  • Test negative paths and failure scenarios.
  • Keep the suite fast and runtime under control.
  • Run tests in CI and fail the build on failures.

Common Testing Mistakes

  • Having no tests at all.
  • Testing implementation instead of behavior.
  • Over-mocking until nothing is really verified.
  • Slow suites that nobody runs.

Key Points

  • Spock's given/when/then reads like documentation.
  • Unit tests need no database; integration tests use @Rollback.
  • Mocks and data-driven tables keep tests fast.
  • Automate tests in CI for lasting safety.
Share this post:

Comments (0)

Please login or register to comment.