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

SpringBoot RestController的Spock异常模拟测试新手求助及示例需求

Got it, let's walk through how to write Spock tests for your Spring Boot RestController, including handling exception scenarios. I'll start with a sample controller setup, then show you the Spock test cases with clear explanations.


Sample Controller & Supporting Classes

First, let's create a simple UserController that fetches a user by ID, along with a custom exception and global exception handler to format error responses (this matches your need to validate exception-based ResponseEntity outputs):

// UserController.groovy
@RestController
@RequestMapping("/api/users")
class UserController {

    private final UserService userService

    // Constructor injection (Spring's preferred approach)
    UserController(UserService userService) {
        this.userService = userService
    }

    @GetMapping("/{id}")
    ResponseEntity<UserDto> getUserById(@PathVariable Long id) {
        UserDto user = userService.findById(id)
        return ResponseEntity.ok(user)
    }
}

// UserService.groovy (simplified interface for example)
interface UserService {
    UserDto findById(Long id)
}

// Custom exception for missing users
class UserNotFoundException extends RuntimeException {
    UserNotFoundException(Long id) {
        super("User with id $id not found")
    }
}

// DTO for error responses
class ErrorResponse {
    int status
    String message

    ErrorResponse(int status, String message) {
        this.status = status
        this.message = message
    }
}

// Global exception handler to convert exceptions to proper ResponseEntity
@RestControllerAdvice
class GlobalExceptionHandler {

    @ExceptionHandler(UserNotFoundException)
    ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException ex) {
        ErrorResponse error = new ErrorResponse(HttpStatus.NOT_FOUND.value(), ex.message)
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error)
    }
}

// User data transfer object
class UserDto {
    Long id
    String name
    String email

    UserDto(Long id, String name, String email) {
        this.id = id
        this.name = name
        this.email = email
    }
}

Spock Test for the Controller

Now, let's write the Spock test using @WebMvcTest (a lightweight slice test for Spring MVC components) to cover both successful and error scenarios:

// UserControllerSpec.groovy
@WebMvcTest(UserController)
class UserControllerSpec extends Specification {

    @Autowired
    MockMvc mockMvc // Simulates HTTP requests to our controller

    @MockBean
    UserService userService // Mocks the dependent service to control its behavior

    def "should return 200 OK and user details when user exists"() {
        given: "a valid user ID and mock user data"
        def userId = 1L
        def expectedUser = new UserDto(userId, "Alice Smith", "alice@example.com")

        when: "we call the GET /api/users/{id} endpoint"
        def response = mockMvc.perform(get("/api/users/{id}", userId)
                .contentType(MediaType.APPLICATION_JSON))

        then: "the user service is called once with the correct ID and returns the mock user"
        1 * userService.findById(userId) >> expectedUser

        and: "the response has 200 status and matches the expected user JSON"
        response.andExpect(status().isOk())
                .andExpect(jsonPath('$.id').value(userId))
                .andExpect(jsonPath('$.name').value("Alice Smith"))
                .andExpect(jsonPath('$.email').value("alice@example.com"))
    }

    def "should return 404 Not Found with error message when user does not exist"() {
        given: "a non-existent user ID"
        def invalidUserId = 99L
        def expectedErrorMsg = "User with id $invalidUserId not found"

        when: "we call the GET /api/users/{id} endpoint with the invalid ID"
        def response = mockMvc.perform(get("/api/users/{id}", invalidUserId)
                .contentType(MediaType.APPLICATION_JSON))

        then: "the user service throws UserNotFoundException"
        1 * userService.findById(invalidUserId) >> { throw new UserNotFoundException(invalidUserId) }

        and: "the response has 404 status and correct error details"
        response.andExpect(status().isNotFound())
                .andExpect(jsonPath('$.status').value(HttpStatus.NOT_FOUND.value()))
                .andExpect(jsonPath('$.message').value(expectedErrorMsg))
    }
}

Key Explanations

Let's break down the important parts:

  1. @WebMvcTest: This annotation loads only the Spring MVC components related to UserController (not the entire application context), making tests fast and focused on the web layer.
  2. @MockBean: We mock the UserService so we can control if it returns a user or throws an exception, without needing a real service implementation.
  3. MockMvc: This tool lets us simulate HTTP requests (like GET) and assert on the response status, headers, and body.
  4. Spock Interaction Testing: The line 1 * userService.findById(userId) >> expectedUser verifies that the service method is called exactly once with the correct ID, and tells it to return our mock user. For the error case, >> { throw new UserNotFoundException(...) } triggers the exception we want to test.
  5. Response Assertions: We use andExpect to check the HTTP status code, and jsonPath to validate specific fields in the JSON response (perfect for verifying your error payload structure).

Required Dependencies

Make sure your build.gradle (Groovy DSL) includes these dependencies for testing:

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.spockframework:spock-core:2.3-groovy-4.0'
testImplementation 'org.spockframework:spock-spring:2.3-groovy-4.0'

That's all! This setup will let you thoroughly test both happy paths and error scenarios for your RestController using Spock's expressive syntax.

内容的提问来源于stack exchange,提问作者Rohan Razdan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 08:10:46