Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 关于如何测试接收字符串并将其放入ArrayList的BufferedReader和FileReader的建议_Java_File Io_Mocking_Tdd_Mockito - Fatal编程技术网

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
  • 尽管有很多方法可以让你的代码可测试

    1。引入构造函数来参数化
    读取文件
    对象创建

    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"));