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

Spring Boot中Mockito对同一Service类部分方法Mock失效问题

问题排查:Spring Boot Controller测试中Service的postPerson Mock失效

我在Java Spring Boot应用中测试Controller类,该Controller依赖一个Service类。测试时创建了listPerson集合,对Service类Mock后,when(.getAPerson).thenReturn(listPersons)可以正常生效,能获取到测试中创建的人员列表;但同一Service类的postPerson方法Mock失效,调用后返回空列表,导致测试testAddPerson时预期返回201实际返回404。

Controller代码

// get a person
@GetMapping(value = "person/{firstNamelastName}")
public ResponseEntity<List<Persons>> getAPerson(@PathVariable String firstNamelastName, HttpServletRequest request,
        HttpServletResponse response) {

    String elemjson = "persons";
    List<Persons> listP = new ArrayList<>();
    try {
        listP = repositorySafetyNetInterface.getAPerson(firstNamelastName, elemjson);
        if (listP.isEmpty()) {
            response.setStatus(204);
            messagelogger = "No Content. The person does not exist."
                    + loggerApiNewSafetyNet.loggerInfo(request, response, firstNamelastName);
            LOGGER.info(messagelogger);
            return ResponseEntity.status(response.getStatus()).build();
        }
    } catch (IOException e) {
        response.setStatus(404);
        return ResponseEntity.status(response.getStatus()).build();
    } catch (NullPointerException nulle) {
        response.setStatus(404);
        return null;
    }

    messagelogger = "OK" + loggerApiNewSafetyNet.loggerInfo(request, response, firstNamelastName);
    LOGGER.info(messagelogger);
    return new ResponseEntity<>(listP, HttpStatus.valueOf(response.getStatus()));
}

// add a person
@PostMapping(value = "/person")
public ResponseEntity addPerson(@RequestBody Persons person, HttpServletRequest request,
HttpServletResponse response) {

    List<Persons> listP = new ArrayList<>();
    try {
        listP = repositoryElementJson.getPersons("persons");
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    listP = repositorySafetyNetInterface.postPerson(person);

    if (listP.isEmpty()) {
        response.setStatus(404);
        messagelogger = "No persons added. " + RESPONSSTATUS + response.getStatus() + ":"
                + loggerApiNewSafetyNet.loggerInfo(request, response, "");
        LOGGER.info(messagelogger);
        return ResponseEntity.status(response.getStatus()).build();
    }

    response.setStatus(201);
    URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{firstName+lastName}")
            .buildAndExpand(person.getFirstName() + person.getLastName()).toUri();
    messagelogger = "A new person is added successful. The URL is : " + location;
    LOGGER.info(messagelogger);
    messagelogger = "Created " + loggerApiNewSafetyNet.loggerInfo(request, response, "");
    LOGGER.info(messagelogger);
    return new ResponseEntity<>(person, HttpStatus.valueOf(response.getStatus()));
}

Service类代码

@Override
public List<Persons> getAPerson(String firstNamelastName, String elemjson) throws IOException {
    List<Persons> listPersons = new ArrayList<>();
    List<Persons> listP = listPersons;
    listPersons = getListsElementsJsonRepository.getPersons(elemjson);

    for (Persons element : listPersons) {
        if ((element.getFirstName().trim() + element.getLastName().trim()).equalsIgnoreCase(firstNamelastName)) {
            listP.add(element);
        }
    }
    LOGGER.debug("Get the person is ok.");
    return listP;
}

@Override
public List<Persons> postPerson(Persons person) {

    if (LOGGER.isDebugEnabled()) {
        LOGGER.debug(loggerApiNewSafetyNet.loggerDebug(person.toString()));
    }

    List<Persons> listPersons;
    listPersons = createListPersons();

    if (LOGGER.isDebugEnabled()) {
        messageLogger = "The persons are: " + listPersons;
        LOGGER.debug(messageLogger);
    }

    boolean findperson = false;
    for (Persons element : listPersons) {
        if ((element.getFirstName() + element.getLastName())
                .equals(person.getFirstName() + person.getLastName())) {
            findperson = true;

            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("This person is already in the list.");
            }
            break;
        }
    }

    if (!findperson) {
        Boolean filecreated = false;
        listPersons.add(person);

        if (LOGGER.isDebugEnabled()) {
            messageLogger = "The person is added in the list: " + listPersons;
            LOGGER.debug(messageLogger);
        }

        List<Firestations> listFirestations;
        listFirestations = createListFirestations();
        List<Medicalrecords> listMedicalrecords;
        listMedicalrecords = createListMedicalrecords();
        newFileJson.createNewFileJson(listPersons, listFirestations, listMedicalrecords, person.toString());
        filecreated = newFileJson.isFileCreated();
        if (Boolean.TRUE.equals(filecreated)) {
            List<Persons> listP;
            listP = verifyIfTheNewPersonInTheFile(person);
            return listP;
        }
    }
    return Collections.emptyList();
}

测试类代码

@WebMvcTest(controllers = NewSafetyAlertController.class)
public class NewSafetyAlertControllerTests {

    private static final Logger LOGGER = LogManager.getLogger(NewSafetyAlertControllerTests.class);

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private SafetyNetInterface safetyNetServiceInterface;


  @Test
    void testAddPerson() throws Exception {
        String firstName = "TEST999_FirstName";
        String lastName = "TEST999_LastName";
        String elemjson = "persons";
        List<Persons> listPersons;
        Persons person;
        person = createPerson(firstName, lastName);
        listPersons = createListPersonsTest(firstName, lastName, person);

        String body = "{\r\n" + "\"firstName\": \"" + firstName + "\",\r\n" + "\"lastName\": \"" + lastName
                + "\",\r\n" + "\"address\": \"1509 Culver St\",\r\n" + "\"city\": \"Culver\",\r\n"
                + "\"zip\": \"97451\",\r\n" + "\"phone\": \"841-874-6512\",\r\n" + "\"email\": \"jaboyd@email.com\"\r\n"
                + "}";
        when(safetyNetServiceInterface.postPerson(person)).thenReturn(listPersons);
        when(safetyNetServiceInterface.getAPerson(firstName + lastName, elemjson)).thenReturn(listPersons);

        mockMvc.perform(post("/person").content(body).contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().is(201));

    }

排查原因

1. 对象匹配问题

Mockito的when()默认使用对象相等性匹配。你在测试中手动创建了person对象,但Controller中通过@RequestBody反序列化得到的是另一个新的Persons实例——这两个对象属性值相同,但不是同一个对象,导致Mock的postPerson(person)不生效,Mockito返回默认的空列表。

而getAPerson方法的参数是字符串,字符串的相等性基于内容判定,所以匹配成功,Mock正常生效。

2. 解决方案

  • 使用参数匹配器:将Mock中的参数替换为any(Persons.class),只要是Persons类型的参数都能匹配:
    when(safetyNetServiceInterface.postPerson(any(Persons.class))).thenReturn(listPersons);
    
  • 重写Persons类的equals和hashCode方法:让属性相同的Persons对象被判定为相等,这样Mockito就能匹配到你手动创建的person对象。
  • 使用refEq匹配器:如果不想重写equals,可以用Mockito.refEq(person)匹配属性相等的对象:
    when(safetyNetServiceInterface.postPerson(refEq(person))).thenReturn(listPersons);
    

3. 额外检查点

  • 确认Controller中调用的repositorySafetyNetInterface就是你Mock的safetyNetServiceInterface,没有注入错误实例。
  • 可以在测试中添加日志或断点,查看Controller接收到的person对象属性是否和手动创建的一致,避免反序列化时出现属性不匹配的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 03:20:25