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

首次编写API单元测试遇AssertionError: JSON路径$.id无值

单元测试报错java.lang.AssertionError: No value at JSON path "$.id"排查

我首次尝试为API编写单元测试时,遇到错误java.lang.AssertionError: No value at JSON path "$.id",但通过OpenApi测试API功能正常。

测试代码

package com.RCTR.usersys;

import com.RCTR.usersys.model.User;
import com.RCTR.usersys.service.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest
@AutoConfigureMockMvc
public class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @MockBean
    private UserService userService;

    @Test
    public void createUserSuccess() throws Exception {
        User newuser = User.builder()
                .id(4L)
                .firstName("Ion")
                .lastName("Popescu")
                .emailId("ion_popescu@gmail.com")
                .build();
        Mockito.when(userService.saveUser(newuser)).thenReturn(newuser);

        String userJson = objectMapper.writeValueAsString(newuser);

        MockHttpServletRequestBuilder mockRequest = MockMvcRequestBuilders.post("/api/v1/user")
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON)
                .content(userJson);

        mockMvc.perform(mockRequest)
                .andDo(MockMvcResultHandlers.print())
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.id").value(4L))
                .andExpect(jsonPath("$.firstName").value("Ion"))
                .andExpect(jsonPath("$.lastName").value("Popescu"))
                .andExpect(jsonPath("$.emailId").value("ion_popescu@gmail.com"));
    }
}

控制器代码

package com.RCTR.usersys.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.RCTR.usersys.model.User;
import com.RCTR.usersys.service.UserService;

@RestController
@RequestMapping("/api/v1/")
public class UserController {
    
private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

@PostMapping("/user")
public User saveUser(@RequestBody User user){
    return userService.saveUser(user);
}

@GetMapping("/users")
public List<User> getAllUsers(){
    
        return userService.getAllUsers();
}

@GetMapping("/users/{id}")
public ResponseEntity<User> getUserById(@PathVariable("id")Long id){
    User user = null;
    user = userService.getUserById(id);
    return ResponseEntity.ok(user);
}

@DeleteMapping("/users/{id}")
public ResponseEntity<Map<String , Boolean>> deleteUser(@PathVariable("id")Long id){
boolean deleted = false;
deleted = userService.deleteUser(id);
Map<String,Boolean> response = new HashMap<>();
 if (deleted) {
        response.put("deleted", true);
        return ResponseEntity.ok(response);
    } else {
        response.put("deleted", false);
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
    }
}

@PutMapping("/users/{id}")
public ResponseEntity<User> updateUser(@PathVariable("id")Long id,@RequestBody User user){
user = userService.updateUser(id,user);
    return ResponseEntity.ok(user);
}

}

问题原因

测试中Mockito.when(userService.saveUser(newuser)).thenReturn(newuser)使用对象实例匹配,但Spring MVC处理请求时会把JSON反序列化为全新的User对象,和你创建的newuser不是同一个实例,导致Mock的方法从未触发,userService.saveUser实际返回null,响应体中没有id字段,触发断言错误。

修复方案

方案1:匹配任意User对象(最简单)

将Mock触发条件改为匹配任意User实例:

Mockito.when(userService.saveUser(Mockito.any(User.class))).thenReturn(newuser);

方案2:基于equals方法匹配(需User类重写equals/hashCode)

如果User类已正确重写equals和hashCode,可以用eq匹配器:

Mockito.when(userService.saveUser(Mockito.eq(newuser))).thenReturn(newuser);

方案3:自定义字段匹配(更严谨)

需要验证传入的User字段符合预期时,用lambda表达式自定义匹配逻辑:

Mockito.when(userService.saveUser(Mockito.argThat(user -> 
    user.getFirstName().equals("Ion") && 
    user.getLastName().equals("Popescu") && 
    user.getEmailId().equals("ion_popescu@gmail.com"))))
.thenReturn(newuser);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 23:31:03