Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/377.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 System.out.println()的JUnit测试_Java_Console_Junit - Fatal编程技术网

Java System.out.println()的JUnit测试

Java System.out.println()的JUnit测试,java,console,junit,Java,Console,Junit,我需要为一个设计拙劣的旧应用程序编写JUnit测试,该应用程序正在向标准输出写入大量错误消息。当getResponse(字符串请求)方法行为正确时,它将返回一个XML响应: @BeforeClass public static void setUpClass() throws Exception { Properties queries = loadPropertiesFile("requests.properties"); Properties responses = load

我需要为一个设计拙劣的旧应用程序编写JUnit测试,该应用程序正在向标准输出写入大量错误消息。当
getResponse(字符串请求)
方法行为正确时,它将返回一个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);
}

您可以通过设置System.out打印流(对于
in
err
)。您可以将其重定向到记录为字符串的打印流,然后检查它吗?这似乎是最简单的机制

(在某个阶段,我主张将应用程序转换为某种日志框架,但我怀疑您已经意识到了这一点!)

使用and System.setXXX很简单:

private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();
private final PrintStream originalOut = System.out;
private final PrintStream originalErr = System.err;

@Before
public void setUpStreams() {
    System.setOut(new PrintStream(outContent));
    System.setErr(new PrintStream(errContent));
}

@After
public void restoreStreams() {
    System.setOut(originalOut);
    System.setErr(originalErr);
}
样本测试用例:

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

@Test
public void err() {
    System.err.print("hello again");
    assertEquals("hello again", errContent.toString());
}
我使用这段代码测试命令行选项(断言-version输出版本字符串,等等)

编辑:
此答案的先前版本在测试后称为
System.settout(null)
;这就是评论员提到的NullPointerException的原因。

@dfa的答案很好,所以我更进一步,使测试输出块成为可能

首先,我创建了
TestHelper
,使用了一个方法
captureOutput
,该方法接受hattermous类
CaptureTest
。captureOutput方法执行设置和分解输出流的工作。调用
CaptureOutput
test
方法时,它可以访问为测试块生成的输出

TestHelper的源代码:

public class TestHelper {

    public static void captureOutput( CaptureTest test ) throws Exception {
        ByteArrayOutputStream outContent = new ByteArrayOutputStream();
        ByteArrayOutputStream errContent = new ByteArrayOutputStream();

        System.setOut(new PrintStream(outContent));
        System.setErr(new PrintStream(errContent));

        test.test( outContent, errContent );

        System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out)));
        System.setErr(new PrintStream(new FileOutputStream(FileDescriptor.out)));

    }
}

abstract class CaptureTest {
    public abstract void test( ByteArrayOutputStream outContent, ByteArrayOutputStream errContent ) throws Exception;
}
请注意,TestHelper和CaptureTest在同一个文件中定义

然后在测试中,您可以导入静态captureOutput。下面是一个使用JUnit的示例:

// imports for junit
import static package.to.TestHelper.*;

public class SimpleTest {

    @Test
    public void testOutput() throws Exception {

        captureOutput( new CaptureTest() {
            @Override
            public void test(ByteArrayOutputStream outContent, ByteArrayOutputStream errContent) throws Exception {

                // code that writes to System.out

                assertEquals( "the expected output\n", outContent.toString() );
            }
        });
}

您不希望重定向system.out流,因为这会重定向整个JVM。JVM上运行的任何其他东西都可能会弄糟。有更好的方法来测试输入/输出。查看存根/模拟。

我知道这是一个旧线程,但有一个很好的库来实现这一点:

文档中的示例:

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)
以及命令行工具需要测试的其他内容。

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

正在生产中

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

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

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

稍微偏离主题,但万一有些人(比如我第一次发现这个线程时)可能对通过SLF4J捕获日志输出感兴趣,的JUnit
@Rule
可能会有帮助:

public class FooTest {
    @Rule
    public final ExpectedLogs logs = new ExpectedLogs() {{
        captureFor(Foo.class, LogLevel.WARN);
    }};

    @Test
    public void barShouldLogWarning() {
        assertThat(logs.isEmpty(), is(true)); // Nothing captured yet.

        // Logic using the class you are capturing logs for:
        Foo foo = new Foo();
        assertThat(foo.bar(), is(not(nullValue())));

        // Assert content of the captured logs:
        assertThat(logs.isEmpty(), is(false));
        assertThat(logs.contains("Your warning message here"), is(true));
    }
}
免责声明

  • 我开发了这个库,因为我找不到任何适合我自己需要的解决方案
  • 目前只有
    log4j
    log4j2
    logback
    的绑定可用,但我很乐意添加更多

使用system.out.println或在使用JUnit时使用记录器api无法直接打印。但是如果您想检查任何值,那么您只需使用

Assert.assertEquals("value", str);
它将抛出以下断言错误:

java.lang.AssertionError: expected [21.92] but found [value]
您的值应该是21.92,现在如果您使用下面的值进行测试,您的测试用例将通过

 Assert.assertEquals(21.92, str);

如果您使用的是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");
}
因为

@Test
void it_prints_out() {

    PrintStream save_out=System.out;final ByteArrayOutputStream out = new ByteArrayOutputStream();System.setOut(new PrintStream(out));

    System.out.println("Hello World!");
    assertEquals("Hello World!\r\n", out.toString());

    System.setOut(save_out);
}
犯错误

@Test
void it_prints_err() {

    PrintStream save_err=System.err;final ByteArrayOutputStream err= new ByteArrayOutputStream();System.setErr(new PrintStream(err));

    System.err.println("Hello World!");
    assertEquals("Hello World!\r\n", err.toString());

    System.setErr(save_err);
}

完整的JUnit 5示例,用于测试
System.out
(替换when部分):

基于和,我想分享我的解决方案,为程序提供输入并测试其输出

作为参考,我使用JUnit4.12

假设我们有一个简单地将输入复制到输出的程序:

import java.util.Scanner;

public class SimpleProgram {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print(scanner.next());
        scanner.close();
    }
}
要测试它,我们可以使用以下类:

import static org.junit.Assert.*;

import java.io.*;

import org.junit.*;

public class SimpleProgramTest {
    private final InputStream systemIn = System.in;
    private final PrintStream systemOut = System.out;

    private ByteArrayInputStream testIn;
    private ByteArrayOutputStream testOut;

    @Before
    public void setUpOutput() {
        testOut = new ByteArrayOutputStream();
        System.setOut(new PrintStream(testOut));
    }

    private void provideInput(String data) {
        testIn = new ByteArrayInputStream(data.getBytes());
        System.setIn(testIn);
    }

    private String getOutput() {
        return testOut.toString();
    }

    @After
    public void restoreSystemInputOutput() {
        System.setIn(systemIn);
        System.setOut(systemOut);
    }

    @Test
    public void testCase1() {
        final String testString = "Hello!";
        provideInput(testString);

        SimpleProgram.main(new String[0]);

        assertEquals(testString, getOutput());
    }
}
我不会解释太多,因为我相信代码是可读的,并且我引用了我的源代码

当JUnit运行
testCase1()
时,它将按照助手方法出现的顺序调用它们:

  • setUpOutput()
    ,因为前面有
    @注释
  • 提供输入(字符串数据)
    ,从
    testCase1()调用
  • getOutput()
    ,从
    testCase1()调用
  • restoreSystemInputOutput()
    ,因为在
    注释之后有
    @

  • 我没有测试
    System.err
    ,因为我不需要它,但它应该很容易实现,类似于测试
    System.out

    如果函数正在打印到System.out,您可以通过使用System.setOut方法更改System.out转到您提供的打印流来捕获该输出。如果创建连接到ByteArrayOutputStream的打印流,则可以将输出捕获为字符串

    // 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());
    

    这是我想到的,但我不敢相信没有标准的JUnit方法可以做到这一点。谢谢,布莱恩。但是实际的工作却得到了dfa的认可。这种方法充满了问题,因为标准输出流是程序所有部分使用的共享资源。最好使用依赖注入来消除对标准输出流的直接使用:是。我支持这一点,甚至可能质疑日志断言(最好断言对日志组件或类似组件的调用)。此外,我使用JUnitMatchers测试响应:assertThat(result,containssString)(“我更喜欢使用System.setOut(null)来恢复流
    // 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());