【问题标题】:Good practice with Mockito And BufferedReaderMockito 和 BufferedReader 的良好实践
【发布时间】:2017-08-23 12:01:37
【问题描述】:

我正在尝试测试一个进行文件/流处理的类。例如,readFile 方法将使用BufferedReader 返回一个字符串的ArrayList,其中包含文件的每一行:

public ArrayList<String> readFile(String fileName)
{
    ArrayList<String> result = new ArrayList<String>();
    FileReader fr = null;
    BufferedReader br = null;
    try {

        fr = new FileReader(STORAGE_DIRECTORY+fileName);
        br = new BufferedReader(fr);
        String sCurrentLine;

        while ((sCurrentLine = br.readLine()) != null) {
            result.add(sCurrentLine);
        }
    }
        catch (FileNotFoundException e) {


            return new ArrayList<String>();
        } catch (IOException e) {

            return new ArrayList<String>();
        }

        br.close();
        fr.close();


        return result;
    }

但是当我使用 Mockito 来模拟 bufferedReader 方法“readLine()”时,由于 FileReader 构造函数而抛出了 FileNotFoundException。我必须使用temporaryFile 还是模拟FileReader 构造函数?

@Test
public void readFileTest5() throws Exception {
    BufferedReader bufferedReader = Mockito.mock(BufferedReader.class);
    FileReader fileReader = Mockito.mock(FileReader.class);
    when(BufferedReader.readLine()).thenReturn("abc");
    assertEquals("",new ArrayList<String>(), FileUtil.readFile("abc"));
}

谢谢

【问题讨论】:

  • 我的错,我应该写全班第一。 BufferedReader 在这里是注入注解(spring 风格)的类的一个实例。 @MockBean private BufferedReader bufferedReader; @MockBean private java.io.FileReader fileReader; @MockBean private File File; @InjectMocks private com.example.system.FileUtil FileUtil;
  • 您应该将缓冲区构造提取到单独的方法中并模拟该方法以返回模拟的 BufferedReader,其“readLine”返回您想要的内容。或者可能不要试图嘲笑这个简单的东西:资源泄漏问题更加明显。
  • 好的,谢谢,“资源泄漏”是什么意思?
  • 发生异常时不要关闭阅读器。
  • 谢谢,我刚刚删除了部分代码以专注于我的问题,但我可以解决这个问题。

标签: java unit-testing testing mockito


【解决方案1】:

readFile BufferedReader 包装 FileReaderFileReader 是在该方法内部创建的,因此您没有机会模拟 FileReader 这意味着您无法模拟输入到您的 BufferedReader 实例中。

这种方法使测试变得困难。

我建议改变你的方法。例如:

public ArrayList<String> readFile(BufferedReader reader) {
    // ...
}

那么你的测试可能是:

@Test
public void readFileTest() throws Exception {
    BufferedReader bufferedReader = Mockito.mock(BufferedReader.class);
    Mockito.when(bufferedReader.readLine()).thenReturn("a", "b", "c", null);
    List<String> expected = Arrays.asList("a", "b", "c");
    Assert.assertEquals("", expected, readFile(bufferedReader));
}

或者根本没有 Mockito:

@Test
public void readFileTest() throws Exception {
    BufferedReader bufferedReader = new BufferedReader(new StringReader("a\nb\nc"));
    List<String> expected = Arrays.asList("a", "b", "c");
    Assert.assertEquals("", expected, readFile(bufferedReader));
}

另一种方法是创建一个实际文件并读取它,即不需要模拟任何东西。您可以使用 JUnit 的 Temporary Folder Rule 来帮助在测试后进行清理。

另外说明:readFile() 并未安全关闭BufferedReaderFileReader

【讨论】:

  • 谢谢,我今天下午看了你的回答。我认为就我而言,它只是将问题从一步转移(我可能有结构问题)。但是我现在在另一个类中有一个使用 readFile() 的方法,调用是这样的:lines = fileUtil.readFile(new BufferedReader(new FileReader(new File(STORAGE_DIRECTORY,FilenameUtils.getName(context))))); 在这个方法调用周围,我使用返回的 List 进行一些处理。但是当我想测试这个方法时,即使我模拟了这个方法,也找不到构造函数中的文件。最好的方法是什么?创建一个临时文件?
  • 我不确定我是否正确地关注了你的上一篇文章,但确实可以避免在这里玩 Mockito 的最简单方法是 (a) 将预先填充的 BufferedReader 传递给 @987654336 @ (就像我在答案中的第二个示例测试中显示的那样)或(b)创建一个真实文件,使用它,然后在您的测试用例中丢弃它(我指向 JUnit 的临时文件夹规则的链接向您展示了一种巧妙的照顾方式创建然后丢弃语义)。
【解决方案2】:

由于您的 FileUtil 确实读取了按名称引用的文件,因此您确实需要在文件系统中创建一个文件。这很容易通过

File tempFile = File.createTempFile("temp", ".tmp");
tempFile.deleteOnExit(true);

关于你的实现代码的一些话:

ArrayList<String> result = new ArrayList<String>();
// combine directory and file name like this
File f = new File(STORAGE_DIRECTORY, fileName);
// use try-with-resource here, like this:
try (BufferedReader br = new BufferedReader(new FileReader(f)))  {

    String sCurrentLine;

    while ((sCurrentLine = br.readLine()) != null) {
        result.add(sCurrentLine);
    }
    } catch (FileNotFoundException e) {
        // no need to create a new list
        return Collections.emptyList()
    } catch (IOException e) {
        return Collections.emptyList()
    }

    // you don't need to close the reader if you use try-with-resource

    return result;
}

您当然可以只使用 Files#readAllLines。

try {
    return Files.readAllLines(Paths.get(STORAGE_DIRECTORY, fileName), StandardCharsets.UTF-8);
} catch (IOException e) {
    return Collections.emptyList();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-27
    • 2017-10-27
    • 2017-05-07
    • 1970-01-01
    相关资源
    最近更新 更多