我需要为一个设计很差的旧应用程序编写JUnit测试,该应用程序正在向标准输出写入大量错误消息。当getResponse(String request)方法行为正确时,它返回一个XML响应:

@BeforeClass
public static void setUpClass() throws Exception {
    Properties queries = loadPropertiesFile("requests.properties");
    Properties responses = loadPropertiesFile("responses.properties");
    instance = new ResponseGenerator(queries, responses);
}

@Test
public void testGetResponse() {
    String request = "<some>request</some>";
    String expResult = "<some>response</some>";
    String result = instance.getResponse(request);
    assertEquals(expResult, result);
}

但是当它得到格式不正确的XML或不理解请求时,它返回null并将一些东西写入标准输出。

在JUnit中是否有方法断言控制台输出?捕捉以下情况:

System.out.println("match found: " + strExpr);
System.out.println("xml not well formed: " + e.getMessage());

当前回答

如果函数打印到系统。输出时,您可以使用系统捕获输出。setOut方法更改系统。输出到由你提供的PrintStream。如果您创建了连接到ByteArrayOutputStream的PrintStream,那么您可以将输出捕获为字符串。

// Create a stream to hold the output
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
// IMPORTANT: Save the old System.out!
PrintStream old = System.out;
// Tell Java to use your special stream
System.setOut(ps);
// Print some output: goes to your special stream
System.out.println("Foofoofoo!");
// Put things back
System.out.flush();
System.setOut(old);
// Show what happened
System.out.println("Here: " + baos.toString());

其他回答

我知道这是一个老线程,但是有一个很好的库可以做到这一点:系统规则 文档中的例子:

public void MyTest {
    @Rule
    public final SystemOutRule systemOutRule = new SystemOutRule().enableLog();

    @Test
    public void overrideProperty() {
        System.out.print("hello world");
        assertEquals("hello world", systemOutRule.getLog());
    }
}

它还允许您捕获System.exit(-1)和命令行工具需要测试的其他内容。

你不想重定向系统。out流,因为它为整个JVM重定向。JVM上运行的任何其他东西都可能被搞砸。有更好的方法来测试输入/输出。查看存根/模拟。

如果您正在使用Spring Boot(您提到您正在使用一个旧的应用程序,所以您可能不是,但它可能对其他人有用),那么您可以以以下方式使用org.springframework.boot.test.rule.OutputCapture:

@Rule
public OutputCapture outputCapture = new OutputCapture();

@Test
public void out() {
    System.out.print("hello");
    assertEquals(outputCapture.toString(), "hello");
}

虽然这个问题已经很老了,而且已经有了很好的答案,但我想提供一个替代方案。我喜欢dfa的答案,但是我想在不同的项目中有一些可重用的东西,而不复制配置,所以我用它创建了一个库,并想贡献给社区。它被称为控制台捕捉器,你可以添加下面的代码片段:

<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>consolecaptor</artifactId>
    <version>1.0.0</version>
    <scope>test</scope>
</dependency>

示例类

public class FooService {

    public void sayHello() {
        System.out.println("Keyboard not responding. Press any key to continue...");
        System.err.println("Congratulations, you are pregnant!");
    }

}

单元测试

import static org.assertj.core.api.Assertions.assertThat;

import nl.altindag.console.ConsoleCaptor;
import org.junit.jupiter.api.Test;

public class FooServiceTest {

    @Test
    public void captureStandardAndErrorOutput() {
        ConsoleCaptor consoleCaptor = new ConsoleCaptor();

        FooService fooService = new FooService();
        fooService.sayHello();

        assertThat(consoleCaptor.getStandardOutput()).contains("Keyboard not responding. Press any key to continue...");
        assertThat(consoleCaptor.getErrorOutput()).contains("Congratulations, you are pregnant!");
        
        consoleCaptor.close();
    }
}

而不是重定向系统。out时,我将重构使用System.out.println()的类,方法是传递一个PrintStream作为合作者,然后使用System.out.println()。在生产和测试间谍在测试。也就是说,使用依赖注入来消除对标准输出流的直接使用。

在生产中

ConsoleWriter writer = new ConsoleWriter(System.out));

在测试中

ByteArrayOutputStream outSpy = new ByteArrayOutputStream();
ConsoleWriter writer = new ConsoleWriter(new PrintStream(outSpy));
writer.printSomething();
assertThat(outSpy.toString(), is("expected output"));

讨论

这样,被测类就可以通过简单的重构进行测试,而不需要间接重定向标准输出或使用系统规则进行模糊拦截。