Java System.out.println() 的 JUnit 测试
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1119385/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JUnit test for System.out.println()
提问by Mike Minicki
I need to write JUnit tests for an old application that's poorly designed and is writing a lot of error messages to standard output. When the getResponse(String request)
method behaves correctly it returns a XML response:
我需要为一个设计不佳的旧应用程序编写 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);
}
But when it gets malformed XML or does not understand the request it returns null
and writes some stuff to standard output.
但是当它出现格式错误的 XML 或不理解请求时,它会返回null
并将一些内容写入标准输出。
Is there any way to assert console output in JUnit? To catch cases like:
有没有办法在 JUnit 中断言控制台输出?捕捉以下情况:
System.out.println("match found: " + strExpr);
System.out.println("xml not well formed: " + e.getMessage());
采纳答案by dfa
using ByteArrayOutputStreamand System.setXXX is simple:
使用ByteArrayOutputStream和 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);
}
sample test cases:
示例测试用例:
@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());
}
I used this code to test the command line option (asserting that -version outputs the version string, etc etc)
我使用此代码来测试命令行选项(断言 -version 输出版本字符串等)
Edit:Prior versions of this answer called System.setOut(null)
after the tests; This is the cause of NullPointerExceptions commenters refer to.
编辑:此答案的先前版本System.setOut(null)
在测试后调用;这是 NullPointerExceptions 评论者引用的原因。
回答by Brian Agnew
You can set the System.out print stream via setOut()(and for in
and err
). Can you redirect this to a print stream that records to a string, and then inspect that ? That would appear to be the simplest mechanism.
您可以通过setOut()(以及 forin
和err
)设置 System.out 打印流。你能把它重定向到一个记录到字符串的打印流,然后检查它吗?这似乎是最简单的机制。
(I would advocate, at some stage, convert the app to some logging framework - but I suspect you already are aware of this!)
(我会提倡在某个阶段将应用程序转换为某种日志记录框架 - 但我怀疑您已经意识到这一点!)
回答by mguymon
@dfa answer is great, so I took it a step farther to make it possible to test blocks of ouput.
@dfa 的回答很棒,所以我更进一步,使测试输出块成为可能。
First I created TestHelper
with a method captureOutput
that accepts the annoymous class CaptureTest
. The captureOutput method does the work of setting and tearing down the output streams. When the implementation of CaptureOutput
's test
method is called, it has access to the output generate for the test block.
首先,我创建TestHelper
了一个captureOutput
接受 annoymous 类的方法CaptureTest
。captureOutput 方法完成设置和拆除输出流的工作。当调用CaptureOutput
的test
方法时,它可以访问测试块的输出 generate。
Source for TestHelper:
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;
}
Note that TestHelper and CaptureTest are defined in the same file.
请注意,TestHelper 和 CaptureTest 定义在同一文件中。
Then in your test, you can import the static captureOutput. Here is an example using JUnit:
然后在您的测试中,您可以导入静态 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() );
}
});
}
回答by Sam Jacobs
You don't want to redirect the system.out stream because that redirects for the ENTIRE JVM. Anything else running on the JVM can get messed up. There are better ways to test input/output. Look into stubs/mocks.
您不想重定向 system.out 流,因为它会重定向整个 JVM。在 JVM 上运行的任何其他东西都可能被搞砸。有更好的方法来测试输入/输出。查看存根/模拟。
回答by Will
I know this is an old thread, but there is a nice library to do this:
我知道这是一个旧线程,但是有一个很好的库可以做到这一点:
Example from the docs:
文档中的示例:
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());
}
}
It will also allow you to trap System.exit(-1)
and other things that a command line tool would need to be tested for.
它还允许您捕获System.exit(-1)
命令行工具需要测试的其他内容。
回答by user1909402
Instead of redirecting System.out
, I would refactor the class that uses System.out.println()
by passing a PrintStream
as a collaborator and then using System.out
in production and a Test Spyin the test. That is, use Dependency Injection to eliminate the direct use of the standard output stream.
System.out
我将System.out.println()
通过传递 aPrintStream
作为合作者然后System.out
在生产中使用和在测试中使用测试间谍来重构使用的类,而不是重定向。即使用依赖注入来消除直接使用标准输出流。
In Production
生产中
ConsoleWriter writer = new ConsoleWriter(System.out));
In the Test
在测试中
ByteArrayOutputStream outSpy = new ByteArrayOutputStream();
ConsoleWriter writer = new ConsoleWriter(new PrintStream(outSpy));
writer.printSomething();
assertThat(outSpy.toString(), is("expected output"));
Discussion
讨论
This way the class under test becomes testable by a simple refactoring, without having the need for indirect redirection of the standard output or obscure interception with a system rule.
这样,被测类就可以通过简单的重构进行测试,而无需对标准输出进行间接重定向或使用系统规则进行模糊拦截。
回答by Marc Carré
Slightly off topic, but in case some people (like me, when I first found this thread) might be interested in capturing log output via SLF4J, commons-testing's JUnit @Rule
might help:
稍微偏离主题,但如果有些人(比如我,当我第一次发现这个线程时)可能对通过 SLF4J 捕获日志输出感兴趣,commons-testing的 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));
}
}
Disclaimer:
免责声明:
- I developed this library since I could not find any suitable solution for my own needs.
- Only bindings for
log4j
,log4j2
andlogback
are available at the moment, but I am happy to add more.
- 我开发了这个库,因为我找不到任何适合我自己需要的解决方案。
- 目前只有
log4j
,log4j2
和 的绑定logback
可用,但我很高兴添加更多。
回答by Affy
You cannot directly print by using system.out.printlnor using logger apiwhile using JUnit. But if you want to check any values then you simply can use
在使用JUnit 时,您不能通过使用system.out.println或使用logger api直接打印。但是,如果您想检查任何值,那么您只需使用
Assert.assertEquals("value", str);
It will throw below assertion error:
它将抛出以下断言错误:
java.lang.AssertionError: expected [21.92] but found [value]
Your value should be 21.92, Now if you will test using this value like below your test case will pass.
您的值应该是 21.92,现在如果您将使用此值进行测试,如下所示,您的测试用例将通过。
Assert.assertEquals(21.92, str);
回答by Disper
If you were using Spring Boot (you mentioned that you're working with an old application, so you probably aren't but it might be of use to others), then you could use org.springframework.boot.test.rule.OutputCapturein the following manner:
如果您使用的是 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");
}
回答by Shimon Doodkin
for out
为了出去
@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);
}
for err
对于错误
@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);
}