You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spock代码块最佳实践:额外遵循规范及相关文档咨询

Spock代码块最佳实践补充指南

Great question! I’ve spent plenty of time iterating on Spock test patterns, so let’s dive into the key guidelines beyond the basic "when/and for actions, then for assertions" rule you already know:

1. Keep the given block focused on setup only

The given block is strictly for initializing your test context—think creating test objects, setting up mocks/stubs, or populating test data. Never put business logic or behavior-triggering method calls here. For example:

given:
def user = new User(id: 1, name: "Alice")
def userRepository = Mock(UserRepository)
userRepository.findById(1) >> Optional.of(user)

// ✅ Good: setup only
def userService = new UserService(userRepository)

Avoid doing anything like userService.updateUser(user) in given—that belongs in when.

2. No side effects in the then block

The then block should only contain assertions, exception validations, or mock interaction verifications. No variable assignments, method calls, or state changes allowed here. Spock enforces this to a degree, but it’s easy to slip up. For example:

then:
// ✅ Good: pure assertion
userService.getUserById(1).name == "Alice"
// ✅ Good: mock verification
1 * userRepository.findById(1)
// ✅ Good: exception check
def ex = thrown(IllegalArgumentException)
ex.message == "Invalid user ID"

// ❌ Bad: side effect in then
def result = userService.getUserById(1) 

3. Use and blocks to improve readability

and is a semantic helper to break up long blocks without changing the test flow. Use it to split setup steps, multiple action calls, or related assertions—this makes your test read like natural language:

given:
def user = new User(id: 1, name: "Alice")
and:
def userRepository = Mock(UserRepository)
userRepository.findById(1) >> Optional.of(user)
and:
def userService = new UserService(userRepository)

when:
def result = userService.getUserById(1)
and:
userService.updateUserName(1, "Alicia")

then:
result.name == "Alice"
and:
1 * userRepository.updateUserName(1, "Alicia")

4. Follow Spock’s native exception testing pattern

Ditch JUnit-style try/catch blocks—Spock has a far cleaner way to test exceptions in the then block. You can even capture the exception to verify its details:

when:
userService.getUserById(-1)

then:
IllegalArgumentException ex = thrown()
ex.message == "User ID must be positive"
// Or for verifying no exception is thrown:
// notThrown(RuntimeException)

Avoid using @ExpectedException—it’s far less flexible than the thrown() method.

5. Data-driven testing best practices with where

When using where for parameterized tests:

  • Use descriptive variable names (skip vague a, b unless the context is crystal clear)
  • Cover edge cases (nulls, empty values, extreme numbers)
  • Keep test cases concise—avoid overcrowding the where block
def "calculate discount for user type"() {
    given:
    def discountService = new DiscountService()

    when:
    def discount = discountService.calculateDiscount(userType, purchaseAmount)

    then:
    discount == expectedDiscount

    where:
    userType         | purchaseAmount | expectedDiscount
    "VIP"            | 100.0          | 20.0
    "REGULAR"        | 100.0          | 5.0
    "NEW"            | 0.0            | 0.0 // Edge case: zero purchase
    null             | 50.0           | 0.0 // Edge case: null user type
}

6. Mock/stub etiquette

  • Initialize mocks/stubs in the given block, not in when or then
  • Define mock interactions appropriately: stub responses (like >>) go in given, interaction count verifications (like *) go in then
  • Avoid over-mocking—only mock external dependencies, never the class under test
given:
def paymentGateway = Mock(PaymentGateway)
paymentGateway.processPayment(_) >> PaymentStatus.SUCCESS
// ✅ Good: stub setup in given

def checkoutService = new CheckoutService(paymentGateway)

when:
def status = checkoutService.completeOrder(order)

then:
status == OrderStatus.COMPLETED
1 * paymentGateway.processPayment(order.amount) // ✅ Good: mock verification in then

7. One test scenario per method

Each test method should verify exactly one behavior or use case. If you find yourself testing multiple unrelated outcomes in a single method, split it into separate tests. This makes failures easier to diagnose and keeps your test suite maintainable.


Reference Resources

The most authoritative source is the Spock Framework Official Reference Documentation, which includes a dedicated section on test structure and best practices. Additionally, many Java/Groovy community resources (like popular tech blogs and conference talks) share real-world Spock patterns from experienced developers.

内容的提问来源于stack exchange,提问作者S.N

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:07:33