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

如何为Java字符串相等校验的菜单项更新逻辑编写JUnit单元测试?

如何为updateMenu方法编写JUnit测试

要给这个方法写单元测试,核心是隔离外部依赖——你的代码直接依赖了Scanner(控制台输入)和menuPrinter,必须用Mock对象替换这些依赖,避免测试依赖真实控制台交互。以下是具体步骤和测试用例:

第一步:重构代码以支持测试

先把Scanner和menuPrinter通过构造方法注入,这样测试时可以替换成Mock实例:

// 假设你的类名为MenuService
public class MenuService {
    private final Scanner scanner;
    private final MenuPrinter menuPrinter;

    // 构造方法注入依赖
    public MenuService(Scanner scanner, MenuPrinter menuPrinter) {
        this.scanner = scanner;
        this.menuPrinter = menuPrinter;
    }

    @Override
    public void updateMenu(DailyMenu dailyMenu) {
        // 保持原方法逻辑不变
    }
}

第二步:用Mockito编写测试用例

Mockito是Java生态最常用的Mock框架,用来模拟依赖、控制输入输出、验证方法调用。

测试场景1:匹配到菜单项并成功更新

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.ArrayList;
import java.util.List;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
public class MenuServiceTest {

    @Mock
    private Scanner scanner;
    @Mock
    private MenuPrinter menuPrinter;

    @Test
    public void updateMenu_whenItemExists_updatesMenuItem() {
        // 1. 准备测试数据
        MenuItem targetItem = new MenuItem("OldName", "OldDesc", "OldImg", 10.0f);
        List<MenuItem> menuItems = new ArrayList<>();
        menuItems.add(targetItem);
        menuItems.add(new MenuItem("OtherItem", "Desc", "Img", 20.0f));
        DailyMenu dailyMenu = new DailyMenu();
        dailyMenu.setMenuItemList(menuItems);

        // 2. 模拟用户输入流程:旧名称→新名称→新描述→新图片→新价格
        when(scanner.nextLine()).thenReturn("OldName", "NewName", "NewDesc", "NewImg");
        when(scanner.nextFloat()).thenReturn(15.0f);
        // 消费nextFloat后的换行符,避免后续输入异常
        doNothing().when(scanner).nextLine();

        // 3. 执行测试方法
        MenuService service = new MenuService(scanner, menuPrinter);
        service.updateMenu(dailyMenu);

        // 4. 验证菜单项属性是否更新
        assert targetItem.getNames().equals("NewName");
        assert targetItem.getDescription().equals("NewDesc");
        assert targetItem.getImage().equals("NewImg");
        assert targetItem.getPrice() == 15.0f;

        // 5. 验证依赖方法是否按预期调用
        verify(menuPrinter).printMenu(dailyMenu);
        verify(scanner, times(4)).nextLine();
        verify(scanner).nextFloat();
    }
}

测试场景2:未匹配到菜单项,无任何修改

@Test
public void updateMenu_whenItemDoesNotExist_noChanges() {
    // 1. 准备测试数据
    MenuItem existingItem = new MenuItem("Existing", "Desc", "Img", 10.0f);
    List<MenuItem> menuItems = new ArrayList<>();
    menuItems.add(existingItem);
    DailyMenu dailyMenu = new DailyMenu();
    dailyMenu.setMenuItemList(menuItems);

    // 2. 模拟输入不存在的菜单项名称
    when(scanner.nextLine()).thenReturn("NonExistentItem");

    // 3. 执行测试方法
    MenuService service = new MenuService(scanner, menuPrinter);
    service.updateMenu(dailyMenu);

    // 4. 验证菜单项属性未被修改
    assert existingItem.getNames().equals("Existing");
    assert existingItem.getPrice() == 10.0f;

    // 5. 验证依赖调用
    verify(menuPrinter).printMenu(dailyMenu);
    verify(scanner).nextLine();
    verifyNoMoreInteractions(scanner);
}

测试场景3:价格输入非法,触发异常处理

@Test
public void updateMenu_whenPriceIsInvalid_handlesException() {
    // 1. 准备测试数据
    MenuItem targetItem = new MenuItem("OldName", "OldDesc", "OldImg", 10.0f);
    List<MenuItem> menuItems = new ArrayList<>();
    menuItems.add(targetItem);
    DailyMenu dailyMenu = new DailyMenu();
    dailyMenu.setMenuItemList(menuItems);

    // 2. 模拟输入:旧名称→新属性→非法价格
    when(scanner.nextLine()).thenReturn("OldName", "NewName", "NewDesc", "NewImg");
    when(scanner.nextFloat()).thenThrow(new InputMismatchException());

    // 3. 执行测试方法
    MenuService service = new MenuService(scanner, menuPrinter);
    service.updateMenu(dailyMenu);

    // 4. 验证异常被捕获,菜单项未被修改
    assert targetItem.getNames().equals("OldName");
}

核心注意事项

  • 依赖注入是前提:必须把Scanner和menuPrinter通过构造方法传入,不能在方法内直接实例化,否则无法替换为Mock对象。
  • 模拟输入要完整:严格按照用户操作流程模拟Scanner的每一次输入,包括处理nextFloat()后的换行符。
  • 双重验证:既要验证业务结果(菜单项属性),也要验证依赖行为(比如menuPrinter是否调用、Scanner方法调用次数)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 13:31:12