Unable to mock BufferedWriter class in junit

前端 未结 3 466
终归单人心
终归单人心 2021-01-15 05:02

I am using a BufferedWriter object in my source code

BufferedWriter outputToErrorFile = new BufferedWriter(new FileWriter(file));
outputToErrorFile.append(\"         


        
相关标签:
3条回答
  • 2021-01-15 05:17

    I wouldn't mock BufferedWritter at all. Create a writer backed by a ByteArrayOutputStream in your test, pass it to the class you are testing, then inspect it when you are done. This may require refactoring your code to create a seam for testing. Here's one possible implementation:

    public void writeToWriter(Writer writer) {
      writer.append("some string");
    }
    

    Then your test can look like this:

    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    OutputStreamWriter writer = new OutputStreamWriter(stream, charset);
    
    objectUnderTest.writeToWriter(writer);
    
    String actualResult = writer.toString(charset);
    assertEquals("some string", actualResult);
    

    If the method you are testing needs to open and close the stream, I'm fond of using Guava's CharSink class:

    public void writeToSink(CharSink sink) {
      try (Writer writer = sink.openBufferedStream()) {
        writer.append("some string");
      }
    }
    
    0 讨论(0)
  • 2021-01-15 05:26

    Just use plain mockito. Since BufferedWriter is not final, there's no need to use PowerMockito here.

    With plain mockito, you can just write:

    final BufferedWriter writer = mock(BufferedWriter.class);
    final IOException exception = new IOException();
    
    doThrow(exception).when(writer).append(anyString());
    

    Of course, you'll have a problem if your BufferedWriter is initialized within your method itself, and you have no method to return it for a given file (and by the way, you should use Files.newBufferedWriter() and a Path).

    Devising a real solution, however, requires that you show the code you are testing.

    0 讨论(0)
  • 2021-01-15 05:27

    You can mock Java IO classes (including their constructors, so future instances also get mocked) with the JMockit library, though you will likely face difficulties such as a NullPointerException from the Writer() constructor (depending on how the mocking was done, and which IO classes were mocked).

    However, note that the Java IO API contains many interacting classes and deep inheritance hierarchies. In your example, the FileWriter class would also probably need to be mocked, otherwise an actual file would get created.

    Also, usage of IO classes in application code is usually just an implementation detail, which can easily be changed. You could switch from IO streams to writers, from regular IO to NIO, or use the new Java 8 utilities, for example. Or use a 3rd-party IO library.

    Bottom line, it's just a terribly bad idea to try and mock IO classes. It's even worse if (as suggested in another answer) you change the client code to have Writers, etc. injected into the SUT. Dependency injection is just not for this kind of thing.

    Instead, use real files in the local file system, preferably from a test directory which can be deleted after the test, and/or use fixed resource files when only reading. Local files are fast and reliable, and lead to more useful tests. Certain developers will say that "a test is not a unit test if it touches the file system", but that's just dogmatic advice.

    0 讨论(0)
提交回复
热议问题