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

JUnit5测试添加学生方法时持续运行无结果,求问题排查与测试方案

问题分析与解决方案

问题背景

我有一个Manager类,其addStudent方法可将新的Student对象添加至ArrayList中,该方法通过控制台接收输入,并对用户输入进行校验(姓名不能为空、手机号需符合格式要求)。我希望测试合法姓名输入、空姓名输入、合法手机号输入等场景,但不清楚如何使用JUnit5实现。我编写了一个测试用例,运行时却一直处于执行状态,没有通过或失败的结果,想了解问题所在及修正方法。


待测试类

import java.util.*;

public class Manager {

    private ArrayList<Student> students;
    Scanner scanner = new Scanner(System.in);

    public void addStudent(){
        String name;
        String mobileNumber;

        // 循环提示用户输入姓名,直到输入非空值
        while(true) {
            System.out.print("Enter student's name: ");
            if (scanner.hasNextLine()) {
                // 去除首尾空白字符
                name = scanner.nextLine().trim();
                if (name.length() > 0) {
                    // 输入非空,退出循环
                    break;
                } else {
                    // 输入为空,提示错误
                    System.out.println("Error: Student name cannot be left blank");
                }
            }
        }

        String phoneRegEx = "^(\\+44\\s?7\\d{3}|\\(?07\\d{3}\\)?)\\s?\\d{3}\\s?\\d{3}$";
        // 循环提示用户输入手机号,直到格式合法
        while (true) {
            System.out.print("Enter student's mobile number: ");
            mobileNumber = scanner.nextLine();

            // 校验是否符合英国手机号格式
            if (mobileNumber.matches(phoneRegEx)) {
                break;
            } else {
                System.out.println("Invalid phone number. Please enter a valid phone number");
            }
        }

        Student student = new Student(name,mobileNumber);
        students.add(student);
        System.out.println("Student added successfully.\n");
    }
}

编写的测试用例

@Test
public void addStudentNameTest() {
    Manager manager = new Manager();
    String testName = "Alice";
    String testMobileNumber = "07979931369";
    System.setIn(new ByteArrayInputStream((testName + System.lineSeparator() + testMobileNumber).getBytes()));

    // 测试添加合法输入的学生
    manager.addStudent();

    // 验证学生是否添加到students列表
    assertTrue(manager.getDoctors().size() == 1);
    assertEquals(testName, manager.getStudent().get(0).getName());
    assertEquals(testMobileNumber, manager.getStudent().get(0).getMobileNumber());
}

问题排查

  1. Scanner绑定时机错误:Manager类中的scanner是成员变量,在类实例化时就已绑定默认的System.in。测试代码中替换System.in的操作在实例化之后,导致scanner仍然监听控制台输入,测试因此卡住等待用户输入。
  2. students列表未初始化:private ArrayList<Student> students;仅声明未初始化,执行students.add(student)会抛出NullPointerException。
  3. 测试用例笔误:manager.getDoctors()、manager.getStudent()均为错误方法名,正确应为getStudents()。
  4. 输入流不完整:模拟输入的手机号后未添加换行符,可能导致scanner.nextLine()无法正确读取结束。

修正方案

1. 重构Manager类,支持依赖注入

修改Manager类,通过构造方法注入Scanner,同时初始化students列表,提升可测试性:

import java.util.ArrayList;
import java.util.Scanner;

public class Manager {

    private ArrayList<Student> students;
    private Scanner scanner;

    // 无参构造,使用默认控制台输入
    public Manager() {
        this(new Scanner(System.in));
    }

    // 带Scanner参数的构造,用于测试注入自定义输入流
    public Manager(Scanner scanner) {
        this.scanner = scanner;
        this.students = new ArrayList<>(); // 初始化学生列表
    }

    public void addStudent(){
        String name;
        String mobileNumber;

        // 姓名校验循环
        while(true) {
            System.out.print("Enter student's name: ");
            if (scanner.hasNextLine()) {
                name = scanner.nextLine().trim();
                if (name.length() > 0) {
                    break;
                } else {
                    System.out.println("Error: Student name cannot be left blank");
                }
            }
        }

        String phoneRegEx = "^(\\+44\\s?7\\d{3}|\\(?07\\d{3}\\)?)\\s?\\d{3}\\s?\\d{3}$";
        // 手机号校验循环
        while (true) {
            System.out.print("Enter student's mobile number: ");
            mobileNumber = scanner.nextLine();

            if (mobileNumber.matches(phoneRegEx)) {
                break;
            } else {
                System.out.println("Invalid phone number. Please enter a valid phone number");
            }
        }

        Student student = new Student(name,mobileNumber);
        students.add(student);
        System.out.println("Student added successfully.\n");
    }

    // 提供获取学生列表的方法,用于测试验证
    public ArrayList<Student> getStudents() {
        return students;
    }
}

2. 修正JUnit5测试用例

通过注入自定义Scanner模拟输入,覆盖多种测试场景:

import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Scanner;
import static org.junit.jupiter.api.Assertions.*;

public class ManagerTest {

    @Test
    public void addStudent_ValidInput_Success() {
        String testName = "Alice";
        String testMobileNumber = "07979931369";
        // 每个输入后添加换行符,模拟用户回车操作
        String input = testName + System.lineSeparator() + testMobileNumber + System.lineSeparator();
        InputStream inputStream = new ByteArrayInputStream(input.getBytes());
        Scanner scanner = new Scanner(inputStream);
        Manager manager = new Manager(scanner);

        manager.addStudent();

        // 验证学生添加成功
        assertEquals(1, manager.getStudents().size());
        assertEquals(testName, manager.getStudents().get(0).getName());
        assertEquals(testMobileNumber, manager.getStudents().get(0).getMobileNumber());
    }

    @Test
    public void addStudent_EmptyName_RetryUntilValid() {
        // 先输入空姓名,再输入合法姓名,最后输入合法手机号
        String input = System.lineSeparator() + "Bob" + System.lineSeparator() + "07123456789" + System.lineSeparator();
        InputStream inputStream = new ByteArrayInputStream(input.getBytes());
        Scanner scanner = new Scanner(inputStream);
        Manager manager = new Manager(scanner);

        manager.addStudent();

        assertEquals(1, manager.getStudents().size());
        assertEquals("Bob", manager.getStudents().get(0).getName());
    }

    @Test
    public void addStudent_InvalidMobile_RetryUntilValid() {
        // 先输入无效手机号,再输入合法手机号
        String input = "Charlie" + System.lineSeparator() + "12345" + System.lineSeparator() + "+44 7987 654 321" + System.lineSeparator();
        InputStream inputStream = new ByteArrayInputStream(input.getBytes());
        Scanner scanner = new Scanner(inputStream);
        Manager manager = new Manager(scanner);

        manager.addStudent();

        assertEquals(1, manager.getStudents().size());
        assertEquals("Charlie", manager.getStudents().get(0).getName());
        assertEquals("+44 7987 654 321", manager.getStudents().get(0).getMobileNumber());
    }
}

3. 关键改进说明

  • 依赖注入Scanner:将输入源的控制权交给外部,测试时可直接传入模拟输入流,彻底解决测试卡住的问题。
  • 初始化学生列表:避免空指针异常,保证方法正常执行。
  • 覆盖多场景测试:新增空姓名、无效手机号的重试逻辑测试,覆盖所有校验场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 01:25:18