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

