Java System.out.println()的JUnit测试
我需要为一个设计拙劣的旧应用程序编写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
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
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());