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

Spring Boot RestTemplate POST调用单元测试及控制器状态码疑问

Hey there! Let's walk through your questions step by step, looking at your controller code, test approach, and REST best practices.

1. Is your UserController ready for unit testing?

Short answer: No, it doesn't meet unit testing standards, and there are critical issues in its implementation.

Problems with your current controller:

  • Bug in response logic: You hardcode a createUser object but return the input user instead of the created one. Clients expect to receive the fully created resource (with generated ID, etc.), not the exact data they sent.
  • No separation of concerns: Real-world controllers should delegate business logic to a service layer. Right now, there’s no meaningful logic to test beyond a hardcoded response, making unit testing pointless.
  • Incorrect HTTP status code: Spring defaults to 200 OK for this method, but creating a resource should return a 201 Created status (more on that later).

Corrected UserController:

We’ll refactor it to use a service layer (so we can mock the service in tests) and return the proper response:

@RestController
@RequestMapping("/users")
public class UserController {

    private final UserService userService;

    // Constructor injection (ideal for testing with mocks)
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        User createdUser = userService.createUser(user);
        // Return 201 Created, plus a Location header pointing to the new resource
        return ResponseEntity.status(HttpStatus.CREATED)
                .header(HttpHeaders.LOCATION, "/users/" + createdUser.getId())
                .body(createdUser);
    }
}

This way, the controller focuses only on HTTP handling, and business logic lives in UserService—which we can easily mock during unit testing.

2. Your unit test isn't actually a unit test

Your current test sends a real HTTP request to localhost:8080, which makes it an integration test, not a unit test. Unit tests for controllers should isolate the controller logic, not spin up a server or hit external endpoints.

Correct unit test with MockMvc:

We’ll use Spring’s @WebMvcTest to load only the controller layer, and Mockito to mock the UserService:

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@WebMvcTest(UserController.class)
public class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper; // Converts objects to JSON for requests

    @MockBean
    private UserService userService; // Mock the service layer

    @Test
    public void testCreateUser_ReturnsCreated() throws Exception {
        // Test data: input user (no ID, since it's generated by the service)
        User inputUser = new User(null, "John", 22, List.of("Hi"));
        // Created user (with generated ID)
        User createdUser = new User(1, "John", 22, List.of("Hi"));

        // Tell the mock service to return our created user when called
        when(userService.createUser(any(User.class))).thenReturn(createdUser);

        // Simulate the POST request
        mockMvc.perform(post("/users")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(inputUser)))
                // Verify the status code is 201 Created
                .andExpect(status().isCreated())
                // Verify the Location header points to the new user's URI
                .andExpect(header().string("Location", "/users/1"))
                // Verify the response body matches the created user
                .andExpect(jsonPath("$.id").value(1))
                .andExpect(jsonPath("$.name").value("John"))
                .andExpect(jsonPath("$.age").value(22))
                .andExpect(jsonPath("$.messages[0]").value("Hi"));
    }
}

Why this works:

  • @WebMvcTest loads only the controller and related beans (no full Spring context), making tests fast.
  • @MockBean replaces the real UserService with a mock, so we can control its behavior without needing a real database or service implementation.
  • MockMvc simulates HTTP requests without starting a server, so we test the controller in isolation.
3. 200 OK or 201 Created?

According to RESTful best practices:

  • 201 Created is the correct status code when you successfully create a new resource. You should also include a Location header with the URI of the newly created resource (as we did in the corrected controller).
  • 200 OK is typically used for requests that retrieve or update existing resources, not for creating new ones.

Your test correctly asserts 201, but your original controller returns 200 by default—fixing the controller to return ResponseEntity.status(HttpStatus.CREATED) will align it with industry standards.


内容的提问来源于stack exchange,提问作者Sweta Sharma

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 17:22:27