测试中有问题的行为:
String filename = "countriesInEurope.txt"
FileReader fr = new FileReader(filename)
因为:
- 文件名是硬编码的,无法在测试中替换
FileReader
使用底层系统 io,难以模拟
尽管如此,仍然有方法可以使您的代码可测试。
1. 引入一个构造函数来参数化 ReadFile
对象的创建。
public class ReadFile {
private String filename;
public ReadFile(String filename) {
this.filename = filename;
}
public List<String> showListOfCourses() throws IOException {
FileReader fr = new FileReader(filename);
...
return courseList;
}
}
在你的测试中,你可以创建一个
ReadFile
对象来使用一些测试文件。采用这种策略可以实现100%的代码行覆盖率,但是你的测试必须访问真实的文件系统上的文件。因此,你不能把它写成一个纯单元测试。
2. 将有问题的代码行提取到一个可重写的方法中。
public class ReadFile {
public List<String> showListOfCourses() throws IOException {
Reader courcesReader = openCoursesFile();
BufferedReader br = new BufferedReader(courcesReader);
List<String> courseList = new ArrayList<>();
return courseList;
}
protected Reader openCoursesFile() throws FileNotFoundException {
return new FileReader("countriesInEurope.txt");
}
}
在你的测试中,你可以继承ReadFile
类并重写Reader openCoursesFile()
方法。例如:
@Test
public void showCources() throws IOException {
ReadFile readFile = new ReadFile() {
protected Reader openCoursesFile() throws java.io.FileNotFoundException {
return new StringReader("Germany\nItaly\nFrance");
};
};
List<String> showListOfCourses = readFile.showListOfCourses();
Assert.assertEquals(Arrays.asList("Germany", "Italy", "France"), showListOfCourses);
}
使用这种策略,你可以把文件访问替换为StringReader
(仅在内存中),从而将测试编写成纯单元测试。唯一不能测试的是
return new FileReader("countriesInEurope.txt");
因此无法实现100%的代码覆盖率。
编辑
3. 引入一个构造函数并传递一个Reader
对象创建
public class ShowListOfCoursesReader {
private Reader reader;
public ReadFile(Reader reader) {
this.reader = reader;
}
public List<String> read() throws IOException {
...
return courseList;
}
}
在您的测试中,您可以创建一个使用传递的读取器的 ShowListOfCoursesReader
对象。读取器也可以是 StringReader
。
采用这种策略,您可以实现100%的代码行覆盖率和纯单元测试。
Reader
对象),则不需要实际文件。模拟Java IO API肯定不是一个好的做法。 - Rogério