Java 关于如何测试接收字符串并将其放入ArrayList的BufferedReader和FileReader的建议
我有一个类,它有一个方法,可以逐行读取文本文件,然后将每一行放入字符串的Java 关于如何测试接收字符串并将其放入ArrayList的BufferedReader和FileReader的建议,java,file-io,mocking,tdd,mockito,Java,File Io,Mocking,Tdd,Mockito,我有一个类,它有一个方法,可以逐行读取文本文件,然后将每一行放入字符串的ArrayList。这是我的密码: public class ReadFile { public List<String> showListOfCourses() throws IOException { String filename = "countriesInEurope.txt"; FileReader fr = new FileReader(filename);
ArrayList
。这是我的密码:
public class ReadFile {
public List<String> showListOfCourses() throws IOException {
String filename = "countriesInEurope.txt";
FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr);
List<String> courseList = new ArrayList<>();
while (true) {
String line = br.readLine();
if (line == null) {
break;
}
courseList.add(line);
}
br.close();
return courseList;
}
}
公共类读取文件{
公共列表showListOfCourses()引发IOException{
字符串filename=“countriesInEurope.txt”;
FileReader fr=新的FileReader(文件名);
BufferedReader br=新的BufferedReader(fr);
List courseList=新建ArrayList();
while(true){
String line=br.readLine();
如果(行==null){
打破
}
课程列表。添加(行);
}
br.close();
返回课程列表;
}
}
我希望得到一些关于如何通过涉及Arrange/Act/Assert的
Mockito
测试此方法的建议。我听说涉及文本文件的阅读器可能很难测试,为其创建临时文件并不是最佳做法,因为它会占用内存?任何建议都将不胜感激。由于文件名countriesInEurope.txt
在您的实现中是硬编码的,因此这是不可测试的。
使该方法可测试的一个好方法是重构该方法,将读取器
作为参数:
public List<String> showListOfCourses(Reader reader) throws IOException {
BufferedReader br = new BufferedReader(reader);
List<String> courseList = new ArrayList<>();
// ...
return courseList;
}
顺便说一句,方法的名称不好,
因为它没有“显示”任何东西。
readListOfCourses
会更有意义。测试中有问题的行是
String filename = "countriesInEurope.txt";
FileReader fr = new FileReader(filename);
因为
FileReader
使用难以模拟的底层系统io读取文件
对象创建
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;
}
}
public class ShowListOfCoursesReader {
private Reader reader;
public ReadFile(Reader reader) {
this.reader = reader;
}
public List<String> read() throws IOException {
// read with reader and transform each line to the
// output object.
// In your case just the line you read, but it could
// also be a date or a address object
...
return courseList;
}
}
在测试中,您可以对ReadFile
类进行子类化,并重写Reader opencoursefile()
方法。例如
@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);
}
所以没有100%的线路覆盖
编辑
3。引入构造函数并向其传递读取器
对象创建
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;
}
}
public class ShowListOfCoursesReader {
private Reader reader;
public ReadFile(Reader reader) {
this.reader = reader;
}
public List<String> read() throws IOException {
// read with reader and transform each line to the
// output object.
// In your case just the line you read, but it could
// also be a date or a address object
...
return courseList;
}
}
课程阅读器的公共类显示列表{
私人读者;
公共读取文件(读卡器){
this.reader=读取器;
}
public List read()引发IOException{
//使用reader阅读,并将每一行转换为
//输出对象。
//在你的情况下,只是你读的那句话,但它可以
//也可以是日期或地址对象
...
返回课程列表;
}
}
在测试中,您可以创建一个ShowListOfCoursesReader
对象,该对象使用传递的读取器。读者也可以是读者。
使用此策略,您可以实现100%的行覆盖率和纯单元测试。提取依赖项,以便在测试时对它们进行模拟/存根和注入。它也有助于缩小课程的范围,使之成为其核心职责
public class CourseReader {
private BufferedReader reader;
public CourseReader(BufferedReader br) {
this.reader = br;
}
public List<String> GetListOfCourses() throws IOException {
List<String> courseList = new ArrayList<>();
String line;
while((line = reader.readLine()) != null) {
courseList.add(line);
}
return courseList;
}
}
并将其用作依赖项
public class CourseReader {
private IReaderWrapper reader;
public CourseReader(IReaderWrapper reader) {
this.reader = reader;
}
public List<String> GetListOfCourses() throws IOException {
List<String> courseList = new ArrayList<>();
String line;
while((line = reader.readLine()) != null) {
courseList.add(line);
}
reader.close();
return courseList;
}
}
公共类课程阅读器{
专用iReader;
公共课程阅读器(iReader阅读器){
this.reader=读取器;
}
public List GetListOfCourses()引发IOException{
List courseList=新建ArrayList();
弦线;
而((line=reader.readLine())!=null){
课程列表。添加(行);
}
reader.close();
返回课程列表;
}
}
这样,测试时只需模拟接口。接口的实现将担心数据的实际读取方式
@Test
public void GetListOfCourses_should_read_3_Courses() {
//Arrange
List<String> expected = Arrays.asList("course1", "course2", "course3");
IReaderWrapper mockedReader = mock(IReaderWrapper.class);
when(mockedReader.readLine())
.thenReturn(expected[0], expected[1], expected[2], null);
CourseReader sut = new CourseReader(mockedReader);
//Act
List<String> actual = sut.GetListOfCourses();
//Assert
assertEquals(expected, actual);
//verify that the close method was called.
verify(mockedReader).close();
}
@测试
公共作废课程列表应阅读课程3{
//安排
预期列表=Arrays.asList(“course1”、“course2”、“course3”);
IReaderWrapper mockedReader=mock(IReaderWrapper.class);
当(mockedReader.readLine())
.thenReturn(应为[0],应为[1],应为[2],null);
CourseReader sut=新CourseReader(mockedReader);
//表演
List actual=sut.GetListOfCourses();
//断言
资产质量(预期、实际);
//验证是否调用了close方法。
验证(mockedReader).close();
}
好吧,您似乎尝试测试框架,在这个特定的案例中是JDK。我会考虑更方便的API:
Files.readAllLines(Paths.get("blablabla.txt"));
或
并通过测试覆盖更高的抽象层—一个使用字符串列表的地方。阅读器是此类的依赖项。OOP的基本规则之一是单一责任。从这个意义上说,为依赖项创建对象不是此类的责任。事实上,创建对象的唯一类(除了DTO或像
BufferedReader
这样的包装器)应该是最初构建程序对象树的工厂或类似的类。除了工厂类和初始值设定项类之外,坚持这种方法几乎总能给您提供良好的可测试类。我不明白为什么这不是“最佳实践”或“耗尽内存”。当然,如果类允许从外部设置数据源(即,给定的读取器
对象),则不需要实际的文件。模拟JavaIOAPI当然不是一个好的做法,只是提醒一下:如果你在练习TDD,那么你首先要编写测试,这样你就不会在代码中考虑如何测试它。
public class CourseReader {
private IReaderWrapper reader;
public CourseReader(IReaderWrapper reader) {
this.reader = reader;
}
public List<String> GetListOfCourses() throws IOException {
List<String> courseList = new ArrayList<>();
String line;
while((line = reader.readLine()) != null) {
courseList.add(line);
}
reader.close();
return courseList;
}
}
@Test
public void GetListOfCourses_should_read_3_Courses() {
//Arrange
List<String> expected = Arrays.asList("course1", "course2", "course3");
IReaderWrapper mockedReader = mock(IReaderWrapper.class);
when(mockedReader.readLine())
.thenReturn(expected[0], expected[1], expected[2], null);
CourseReader sut = new CourseReader(mockedReader);
//Act
List<String> actual = sut.GetListOfCourses();
//Assert
assertEquals(expected, actual);
//verify that the close method was called.
verify(mockedReader).close();
}
Files.readAllLines(Paths.get("blablabla.txt"));
Files.lines(Paths.get("blablabla.txt"));