Redirect console output to string in Java

I have one method whose return type is void and it prints directly on console.

However I need that output in a String so that I can work on it.

As I can't make any changes to the method with return type void I have to redirect that output to a String.

How can I redirect it in Java?

119256 次浏览

If the function is printing to System.out, you can capture that output by using the System.setOut method to change System.out to go to a PrintStream provided by you. If you create a PrintStream connected to a ByteArrayOutputStream, then you can capture the output as a String.

Example:

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

This program prints just one line:

Here: Foofoofoo!

Here is a utility Class named ConsoleOutputCapturer. It allows the output to go to the existing console however behind the scene keeps capturing the output text. You can control what to capture with the start/stop methods. In other words call start to start capturing the console output and once you are done capturing you can call the stop method which returns a String value holding the console output for the time window between start-stop calls. This class is not thread-safe though.

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.List;


public class ConsoleOutputCapturer {
private ByteArrayOutputStream baos;
private PrintStream previous;
private boolean capturing;


public void start() {
if (capturing) {
return;
}


capturing = true;
previous = System.out;
baos = new ByteArrayOutputStream();


OutputStream outputStreamCombiner =
new OutputStreamCombiner(Arrays.asList(previous, baos));
PrintStream custom = new PrintStream(outputStreamCombiner);


System.setOut(custom);
}


public String stop() {
if (!capturing) {
return "";
}


System.setOut(previous);


String capturedValue = baos.toString();


baos = null;
previous = null;
capturing = false;


return capturedValue;
}


private static class OutputStreamCombiner extends OutputStream {
private List<OutputStream> outputStreams;


public OutputStreamCombiner(List<OutputStream> outputStreams) {
this.outputStreams = outputStreams;
}


public void write(int b) throws IOException {
for (OutputStream os : outputStreams) {
os.write(b);
}
}


public void flush() throws IOException {
for (OutputStream os : outputStreams) {
os.flush();
}
}


public void close() throws IOException {
for (OutputStream os : outputStreams) {
os.close();
}
}
}
}

Although this question is very old and has already very good answers I want to provide an alternative. I created a library specifically for this use case. It is called Console Captor and you can add it with the following snippet:

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

Example class

public class FooService {


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


}

Unit test

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

If you are using Spring Framework, there is a really easy way to do this with OutputCaptureExtension:

 @ExtendWith(OutputCaptureExtension.class)
class MyTest {


@Test
void test(CapturedOutput output) {
System.out.println("ok");
assertThat(output).contains("ok");
System.err.println("error");
}


@AfterEach
void after(CapturedOutput output) {
assertThat(output.getOut()).contains("ok");
assertThat(output.getErr()).contains("error");
}


}