如何在java中编写用于刷新链式流的单元测试



我正在构建一个简单的记录器类-

public class MyLogger {
private final PrintWriter errorWriter;
public MyLogger(OutputStream outputStream) {
final Writer errorStreamWriter = new OutputStreamWriter(outputStream);
this.errorWriter = new PrintWriter(errorStreamWriter);
}

public void start() {
errorWriter.flush();
}

public void addError(String errorMessage) {
errorWriter.println(errorMessage);
}

public void finish() {
errorWriter.flush();
errorWriter.close();
}
}

现在我想写一个单元测试来测试流是否被刷新,简而言之,如果我们评论以下方法-

public void start() {   
// errorWriter.flush();
}                       

public void finish() {  
// errorWriter.flush();
// errorWriter.close();
}                       

那么测试应该会失败,我不希望使用反射,即使它提供了一个解决方案,我觉得这是不可能的,因为我们无法控制errorWriter,当链式流被刷新时,其他连接的流也不会被刷新,但如果有解决方案,请告诉我。

有一些方法可以测试它。我在示例中使用Mockito作为嘲讽框架。

我更喜欢解决方案1(所有其他或多或少都是技巧,但该模式对于使用数据库连接或其他昂贵对象的更困难的类可能很有用。

  1. 针对类的公共API进行测试(就像一个黑盒(。调用方对内部PrintWriter一无所知,也不需要了解任何信息。但是,您可以编写一个测试来确保OutputStream被刷新,因为它是传播的(至少使用标准Java Streams(。如果您在start方法中注释掉flush,这将失败。

    class MyLoggerTest {
    OutputStream out = mock(OutputStream.class);
    MyLogger myLogger = new MyLogger(out);
    @Test
    void should_call_flush_out_stream() throws IOException {
    myLogger.start();
    verify(out).flush();
    }
    }
    
  2. 您将类更改为具有第二个受保护的构造函数,该构造函数接受PrintWriter。公共构造函数使用这个构造函数,您在测试中使用mock和第二个构造函数进行刷新。该测试与1(中的测试类似,但您使用的是mock(printWriter.class)

    public class MyLogger {
    private final PrintWriter errorWriter;
    public MyLogger(OutputStream outputStream) {
    this(new PrintWriter(new OutputStreamWriter(outputStream)));
    }
    MyLogger(PrintWriter writer) {
    this.errorWriter = writer;
    }
    ....
    
  1. 您没有直接从成员变量访问PrintWriter,而是创建了一个内部getter方法,然后可以在Spy中使用该方法来更改内部表示。

    public class MyLogger {
    private final PrintWriter errorWriter;
    ...Constructor...
    PrintWriter getWriter() {
    return errorWriter;
    }
    public void start() {
    getWriter().flush();
    }
    ...
    }
    

测试结果如下:

class MyLoggerTest {
OutputStream out = mock(OutputStream.class);
PrintWriter writer = mock(PrintWriter.class);
MyLogger myLogger = spy(new MyLogger(out));
@BeforeEach
void setup() {
when(myLogger.getWriter()).thenReturn(writer);
}
@Test
void should_call_flush_out_stream() {
myLogger.start();
verify(writer).flush();
}
}

最新更新