构造方法异常测试未达预期,如何捕获FileNotFoundException?
我有一段从CSV文件读取数据的代码,需要编写单元测试,但不知如何着手。
原代码如下:
public class BudgetTags implements BudgetTagsList{ // State variables private Set<String> tags = new TreeSet<>(); private String tag_file_path; public BudgetTags(String tag_file_path){ //Retrieve tags from tag file this.tag_file_path = tag_file_path; this.retrieveTags(); } public void retrieveTags() { String line = ""; try{ // Begin reading each line BufferedReader br = new BufferedReader(new FileReader(this.tag_file_path )); while((line = br.readLine()) != null){ String[] row = line.split(","); this.tags.add(row[0]); //Assume correct file format } br.close(); } catch (IOException e){ System.out.println("Fatal exception: "+ e.getMessage()); } } }
需注意,retrieveTags()方法因FileNotFoundException继承自IOException,无法单独声明抛出该异常。我当前的测试代码如下:
@Test @DisplayName("File name doesn't exist") void testRetrieveTag3() { String path = "test\\no_file.csv"; //Instantiate new tags object BudgetTags tags = new BudgetTags(path); IOException thrown = assertThrows(IOException.class, () -> tags.retrieveTags()); }
由于path指向的文件不存在,我预期测试能捕获IOException(更希望捕获FileNotFoundException),但运行测试时却收到AssertionFailedError。请问如何重构测试,以在实例化BudgetTags对象时捕获FileNotFoundException?
问题根源是retrieveTags()方法内部捕获了IOException但没有重新抛出,测试代码调用它时根本不会向外抛出异常,assertThrows自然会失败。要解决这个问题,需要先调整原代码的异常处理逻辑,再修改测试:
步骤1:调整原代码的异常处理
有两种可选的处理方式:
方式一:将异常向上抛出
修改retrieveTags()的方法签名,声明抛出IOException(自动包含FileNotFoundException子类),同时去掉内部的catch块,或在catch中重新抛出异常:public void retrieveTags() throws IOException { String line = ""; try (BufferedReader br = new BufferedReader(new FileReader(this.tag_file_path))) { while((line = br.readLine()) != null){ String[] row = line.split(","); this.tags.add(row[0]); } } // 若需要保留日志,可改为: // try { ... } catch (IOException e) { // System.out.println("Fatal exception: "+ e.getMessage()); // throw e; // } }同时构造方法也需要声明抛出
IOException,因为它调用了retrieveTags():public BudgetTags(String tag_file_path) throws IOException { this.tag_file_path = tag_file_path; this.retrieveTags(); }方式二:保存异常状态(不抛出检查型异常)
如果不想抛出检查型异常,可以在类中添加成员变量保存异常状态:private IOException loadException; public void retrieveTags() { String line = ""; loadException = null; try (BufferedReader br = new BufferedReader(new FileReader(this.tag_file_path))) { while((line = br.readLine()) != null){ String[] row = line.split(","); this.tags.add(row[0]); } } catch (IOException e){ loadException = e; System.out.println("Fatal exception: "+ e.getMessage()); } } public IOException getLoadException() { return loadException; }
步骤2:修改单元测试
根据上面的代码调整,测试也需要对应修改:
对应方式一的测试代码
构造方法会抛出异常,直接在构造对象时捕获即可:
@Test @DisplayName("File name doesn't exist - should throw FileNotFoundException") void testRetrieveTag3() { String path = "test\\no_file.csv"; FileNotFoundException thrown = assertThrows(FileNotFoundException.class, () -> { new BudgetTags(path); // 构造时调用retrieveTags,会抛出异常 }); // 可选:验证异常信息 assertNotNull(thrown.getMessage()); }
对应方式二的测试代码
不需要捕获异常,而是检查保存的异常状态:
@Test @DisplayName("File name doesn't exist - should record FileNotFoundException") void testRetrieveTag3() { String path = "test\\no_file.csv"; BudgetTags tags = new BudgetTags(path); assertNotNull(tags.getLoadException()); assertTrue(tags.getLoadException() instanceof FileNotFoundException); }
额外建议
尽量使用try-with-resources语法简化IO资源管理,它会自动关闭资源,避免手动关闭的遗漏,代码更简洁安全。
内容的提问来源于stack exchange,提问作者J_code

