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

如何为Spring Boot Service的updateCar方法实现单元测试全覆盖

Spring Boot Service updateCar方法单元测试全覆盖方案

问题背景

我正在为Spring Boot Service编写单元测试,希望实现updateCar方法的全覆盖。

目标方法(updateCar)

public void updateCar(String id, String carModel, Integer HP, Integer year, String designer) {
    Garage garage = garageRepository.findById(id)
            .orElseThrow(() -> new IllegalStateException(
                    "A car with the id " + id + " is not in our Garage."));
    if(carModel != null && carModel.length() > 0 && !Objects.equals(garage.getCarModel(), carModel)) {
        garage.setCarModel(carModel);
    }
    if(HP != null && !Objects.equals(garage.getHP(), HP)) {
        garage.setHP(HP);
    }
    if(year != null && !Objects.equals(garage.getYear(), year)) {
        garage.setYear(year);
    }
    if(designer != null && designer.length() > 0 && !Objects.equals(garage.getDesigner(), designer)) {
        garage.setDesigner(designer);
    }
    garageRepository.save(garage);
}

当前测试代码

@Test
@DisplayName("Update Car Test")
void testUpdateCar() {

    Garage updatedGarage = new Garage();
    String id = "630ca281f12905d5f5249f08";
    String carModel = "Updated carModel";
    int HP = 480;
    int year = 1964;
    String designer = "Updated designer";

    updatedGarage.setId(id);
    updatedGarage.setCarModel(carModel);
    updatedGarage.setHP(HP);
    updatedGarage.setYear(year);
    updatedGarage.setDesigner(designer);

    when(garageRepository.findById(id))
            .thenReturn(Optional.of(updatedGarage));
    garageService.updateCar(id, carModel, HP, year, designer);

    when(garageRepository.save(updatedGarage))
            .thenReturn(updatedGarage);
    verify(garageRepository).findById(id);
}

当前问题

当前测试仅获得部分覆盖率,未覆盖updateCar方法中的garage.setCarModel(carModel)、garage.setHP(HP)、garage.setYear(year)以及garage.setDesigner(designer)逻辑,请问如何实现该方法的测试全覆盖?


全覆盖测试方案

要覆盖所有分支逻辑,需要针对每个字段的更新条件、不更新条件,以及findById返回空的异常场景分别编写测试用例。

1. 覆盖字段更新的核心场景

当前测试中传入的参数和模拟的Garage对象原有值完全一致,导致if条件不成立,自然不会执行set方法。需要让传入的参数和原有值不同:

@Test
@DisplayName("Update all fields successfully when values are different")
void testUpdateAllFields() {
    // 初始化原有车库对象,设置初始值
    String id = "630ca281f12905d5f5249f08";
    Garage existingGarage = new Garage();
    existingGarage.setId(id);
    existingGarage.setCarModel("Original Model");
    existingGarage.setHP(300);
    existingGarage.setYear(1960);
    existingGarage.setDesigner("Original Designer");

    // 模拟Repository返回原有对象
    when(garageRepository.findById(id)).thenReturn(Optional.of(existingGarage));

    // 传入不同的更新参数
    String newModel = "Updated carModel";
    Integer newHP = 480;
    Integer newYear = 1964;
    String newDesigner = "Updated designer";

    garageService.updateCar(id, newModel, newHP, newYear, newDesigner);

    // 验证每个字段都被正确更新
    assertEquals(newModel, existingGarage.getCarModel());
    assertEquals(newHP, existingGarage.getHP());
    assertEquals(newYear, existingGarage.getYear());
    assertEquals(newDesigner, existingGarage.getDesigner());

    // 验证save方法被调用
    verify(garageRepository).save(existingGarage);
}

2. 覆盖字段不更新的分支场景

针对每个字段的if条件不成立的情况(参数为空、空字符串、与原有值相同),分别编写测试:

针对carModel的不更新测试

@Test
@DisplayName("Do not update carModel when new value matches original")
void testNotUpdateCarModelWhenSame() {
    String id = "630ca281f12905d5f5249f08";
    Garage existingGarage = new Garage();
    existingGarage.setId(id);
    String originalModel = "Original Model";
    existingGarage.setCarModel(originalModel);

    when(garageRepository.findById(id)).thenReturn(Optional.of(existingGarage));

    // 传入和原有值相同的carModel
    garageService.updateCar(id, originalModel, null, null, null);

    // 验证carModel未被修改
    assertEquals(originalModel, existingGarage.getCarModel());
    verify(garageRepository).save(existingGarage);
}

@Test
@DisplayName("Do not update carModel when new value is empty string")
void testNotUpdateCarModelWhenEmpty() {
    String id = "630ca281f12905d5f5249f08";
    Garage existingGarage = new Garage();
    existingGarage.setId(id);
    existingGarage.setCarModel("Original Model");

    when(garageRepository.findById(id)).thenReturn(Optional.of(existingGarage));

    // 传入空字符串
    garageService.updateCar(id, "", null, null, null);

    assertEquals("Original Model", existingGarage.getCarModel());
    verify(garageRepository).save(existingGarage);
}

针对HP的不更新测试

@Test
@DisplayName("Do not update HP when new value matches original")
void testNotUpdateHPWhenSame() {
    String id = "630ca281f12905d5f5249f08";
    Garage existingGarage = new Garage();
    existingGarage.setId(id);
    Integer originalHP = 300;
    existingGarage.setHP(originalHP);

    when(garageRepository.findById(id)).thenReturn(Optional.of(existingGarage));

    garageService.updateCar(id, null, originalHP, null, null);

    assertEquals(originalHP, existingGarage.getHP());
    verify(garageRepository).save(existingGarage);
}

@Test
@DisplayName("Do not update HP when new value is null")
void testNotUpdateHPWhenNull() {
    String id = "630ca281f12905d5f5249f08";
    Garage existingGarage = new Garage();
    existingGarage.setId(id);
    existingGarage.setHP(300);

    when(garageRepository.findById(id)).thenReturn(Optional.of(existingGarage));

    garageService.updateCar(id, null, null, null, null);

    assertEquals(300, existingGarage.getHP());
    verify(garageRepository).save(existingGarage);
}

同理,为year和designer编写上述类似的不更新场景测试即可覆盖对应分支。

3. 覆盖找不到车辆的异常场景

@Test
@DisplayName("Throw exception when car not found by id")
void testUpdateCarNotFound() {
    String id = "non-existent-id";
    when(garageRepository.findById(id)).thenReturn(Optional.empty());

    // 验证抛出指定异常
    IllegalStateException exception = assertThrows(IllegalStateException.class, () -> {
        garageService.updateCar(id, "New Model", 500, 2020, "New Designer");
    });

    assertEquals("A car with the id " + id + " is not in our Garage.", exception.getMessage());
    // 验证save方法未被调用
    verify(garageRepository, never()).save(any(Garage.class));
}

4. 混合场景测试(部分字段更新)

测试部分字段更新、部分不更新的情况,确保逻辑兼容:

@Test
@DisplayName("Update only partial fields")
void testUpdatePartialFields() {
    String id = "630ca281f12905d5f5249f08";
    Garage existingGarage = new Garage();
    existingGarage.setId(id);
    existingGarage.setCarModel("Original Model");
    existingGarage.setHP(300);
    existingGarage.setYear(1960);
    existingGarage.setDesigner("Original Designer");

    when(garageRepository.findById(id)).thenReturn(Optional.of(existingGarage));

    // 只更新HP和year,其他参数保持原有值或为空
    Integer newHP = 480;
    Integer newYear = 1964;
    garageService.updateCar(id, "Original Model", newHP, newYear, "");

    // 验证HP和year被更新,其他字段不变
    assertEquals("Original Model", existingGarage.getCarModel());
    assertEquals(newHP, existingGarage.getHP());
    assertEquals(newYear, existingGarage.getYear());
    assertEquals("Original Designer", existingGarage.getDesigner());

    verify(garageRepository).save(existingGarage);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 21:35:23