I have a Java command-line program. I would like to create JUnit test case to be able to simulate System.in. Because when my program runs it will get into the while loop and waits for input from users. How do I simulate that in JUnit?
Thanks
It is technically possible to switch System.in, but in general, it would be more robust not to call it directly in your code, but add a layer of indirection so the input source is controlled from one point in your application. Exactly how you do that is an implementation detail - the suggestions of dependency injection are fine, but you don't necessarily need to introduce 3rd party frameworks; you could pass round an I/O context from the calling code, for example.
How to switch System.in:
String data = "Hello, World!\r\n";
InputStream stdin = System.in;
try {
System.setIn(new ByteArrayInputStream(data.getBytes()));
Scanner scanner = new Scanner(System.in);
System.out.println(scanner.nextLine());
} finally {
System.setIn(stdin);
}
Based on #McDowell's answer and another answer that shows how to test System.out, I would like to share my solution to give an input to a program and test its output.
As a reference, I use JUnit 4.12.
Let's say we have this program that simply replicates input to output:
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();
}
}
To test it, we can use the following class:
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());
}
}
I won't explain much, because I believe the code is readable and I cited my sources.
When JUnit runs testCase1(), it is going to call the helper methods in the order they appear:
setUpOutput(), because of the #Before annotation
provideInput(String data), called from testCase1()
getOutput(), called from testCase1()
restoreSystemInputOutput(), because of the #After annotation
I didn't test System.err because I didn't need it, but it should be easy to implement, similar to testing System.out.
There are a few ways to approach this. The most complete way is to pass in an InputStream while running the class under test which is a fake InputStream which passes simulated data to your class. You can look at a dependency injection framework (such as Google Guice) if you need to do this a lot in your code, but the simple way is:
public class MyClass {
private InputStream systemIn;
public MyClass() {
this(System.in);
}
public MyClass(InputStream in) {
systemIn = in;
}
}
Under test you would call the constructor that takes the input stream. You cloud even make that constructor package private and put the test in the same package, so that other code would not generally consider using it.
Try to refactor your code to use dependency injection. Instead of having your a method that uses System.in directly, have the method accept an InputStream as an argument. Then in your junit test, you'll be able to pass a test InputStream implementation in place of System.in.
You can write a clear test for the command line interface by using the TextFromStandardInputStream rule of the System Rules library.
public void MyTest {
#Rule
public final TextFromStandardInputStream systemInMock
= emptyStandardInputStream();
#Test
public void readTextFromStandardInputStream() {
systemInMock.provideLines("foo");
Scanner scanner = new Scanner(System.in);
assertEquals("foo", scanner.nextLine());
}
}
Full disclosure: I'm the author of that library.
You could create a custom InputStream and attach it to the System class
class FakeInputStream extends InputStream {
public int read() {
return -1;
}
}
And then use it with your Scanner
System.in = new FakeInputStream();
Before:
InputStream in = System.in;
...
Scanner scanner = new Scanner( in );
After:
InputStream in = new FakeInputStream();
...
Scanner scanner = new Scanner( in );
Although I think you should better to test how your class should work with the data read from the input stream and not really how it reads from there.
The problem with BufferedReader.readLine() is that it is a blocking method which waits for user input. It seems to me that you don't particularly want to simulate that (i.e. you want tests to be fast). But in a testing context it continually returns null at high speed during testing, which is irksome.
For a purist you can make the getInputLine below package-private, and mock it: easy-peezy.
String getInputLine() throws Exception {
return br.readLine();
}
... you'd have to make sure that you had a way of stopping (typically) a loop of user interaction with the app. You'd also have to cope with the fact that your "input lines" would always be the same until you somehow changed the doReturn of your mock: hardly typical of user input.
For a non-purist who wishes to make life easy for themselves (and produce readable tests) you could put all this stuff below in your app code:
private Deque<String> inputLinesDeque;
void setInputLines(List<String> inputLines) {
inputLinesDeque = new ArrayDeque<String>(inputLines);
}
private String getInputLine() throws Exception {
if (inputLinesDeque == null) {
// ... i.e. normal case, during app run: this is then a blocking method
return br.readLine();
}
String nextLine = null;
try {
nextLine = inputLinesDeque.pop();
} catch (NoSuchElementException e) {
// when the Deque runs dry the line returned is a "poison pill",
// signalling to the caller method that the input is finished
return "q";
}
return nextLine;
}
... in your test you might then go like this:
consoleHandler.setInputLines( Arrays.asList( new String[]{ "first input line", "second input line" }));
before triggering off the method in this "ConsoleHandler" class which needs input lines.
maybe like this (not tested):
InputStream save_in=System.in;final PipedOutputStream in = new PipedOutputStream(); System.setIn(new PipedInputStream(in));
in.write("text".getBytes("utf-8"));
System.setIn( save_in );
more parts:
//PrintStream save_out=System.out;final ByteArrayOutputStream out = new ByteArrayOutputStream();System.setOut(new PrintStream(out));
InputStream save_in=System.in;final PipedOutputStream in = new PipedOutputStream(); System.setIn(new PipedInputStream(in));
//start something that reads stdin probably in a new thread
// Thread thread=new Thread(new Runnable() {
// #Override
// public void run() {
// CoursesApiApp.main(new String[]{});
// }
// });
// thread.start();
//maybe wait or read the output
// for(int limit=0; limit<60 && not_ready ; limit++)
// {
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
in.write("text".getBytes("utf-8"));
System.setIn( save_in );
//System.setOut(save_out);
#Stefan Birkner, Thanks!
Modify Pom.xml
Ref:
https://stackoverflow.com/a/66127606/8317677
https://github.com/stefanbirkner/system-lambda/blob/master/pom.xml
https://github.com/stefanbirkner/system-lambda/blob/master/src/test/java/com/github/stefanbirkner/systemlambda/WithTextFromSystemInTest.java
<properties>
<system-lambda.version>1.2.1</system-lambda.version>
</properties>
<dependencies>
<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-lambda</artifactId>
<version>${system-lambda.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
Add function code
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class SimpleProgram003 {
public static void main(String[] args) {
try{
String c;
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
do{
c = in.readLine();
System.out.println(c);
String d = c;
}while(!c.equals("q"));
}catch(Exception e){
System.out.println("catch Exception");
}
}
}
Add test code
import static com.github.stefanbirkner.systemlambda.SystemLambda.*;
import static org.junit.Assert.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintStream;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* Unit test for simple App. JUnit 4.x.
*/
public class SimpleProgram003Test {
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 setInput(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 1\nq\n";
setInput(testString);
SimpleProgram003.main(new String[0]);
// String a = getOutput();
assertEquals("Hello 1\r\nq\r\n", getOutput());
}
#Test // Multiply inputs
public void testCase2() throws Exception {
withTextFromSystemIn(
"Input1",
"Input2",
"q",
"Input3"
).execute(() -> {
SimpleProgram003.main(new String[0]);
// String a = getOutput();
assertEquals("Input1\r\nInput2\r\nq\r\n", getOutput());
});
}
}
Related
I'm new to java and I'm having a little problem with my code. There's no error and such, it just keeps saying process finished but no output was displayed. The filename is correct as I've checked.
import java.nio.file.;
import java.io.;
public class GuessingGame {
public GuessingGame() {
String filename = "C:\\Users\\angela\\Documents\\words.txt";
Path path = Paths.get(filename.toString());
try {
InputStream input = Files.newInputStream(path);
BufferedReader read = new BufferedReader(new InputStreamReader(input));
String word = null;
while((word = read.readLine()) !=null) {
System.out.println(word);
}
}
catch(IOException ex) {
}
}
public static void main (String[] args) {
new GuessingGame();
}
}
You are ignoring the exception and you don't close the file. Save some typing by using the built-in input.transferTo() for copying the file to System.out, and pass on the exception for the caller to handle by adding throws IOException to constructor and main.
Replace your try-catch block with this try-with-resources, which handles closing the file after use:
try (InputStream input = Files.newInputStream(path)) {
input.transferTo(System.out) ;
}
You managed to call the intended class, but you also needed to specify the specific function which you have declared in the function. Like so:
public static void main (String[] args) { GuessingGame gg = new GuessingGame; gg.GuessingGame(); }
After a long period of searching for my problem, I have no other idea, I have to ask here.
I search for a method to read the console output or System.out to a JavaFX TextArea but I don't know how I get those strings.
I want to put that into an external Thread:
package Threads;
import Core.Engine;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.Scanner;
public class ConsoleListenerThread implements Runnable {
Engine engine;
public ConsoleListenerThread(Engine engine) {
this.engine = engine;
}
#Override
public void run() {
String line;
while (true){
}
}
}
Update:
I really need the console output...what you see when you start a program. And how I get there is unnecessary I think (Startup etx), because I just want a String whenever the console prints a new output.
Example:
System.out.println("Hello");
And then my thread revive this string:
"hello"
If you are printing directly to the console with the System.out.println() method, then you already have the string. If , on the other hand, you are calling a different method that prints to the console from another object, try having that method return a String instead so that you can store that data.
So I solved this problem, so here is the code:
package Threads;
import Core.Engine;
import java.io.*;
public class ConsolListenerThrad implements Runnable {
Engine engine;
public ConsolListenerThrad(Engine engine) {
this.engine = engine;
}
#Override
public void run() {
String line = null;
PipedOutputStream pOut = new PipedOutputStream();
System.setOut(new PrintStream(pOut));
PipedInputStream pIn = null;
try {
pIn = new PipedInputStream(pOut);
} catch (IOException e) {
e.printStackTrace();
}
BufferedReader reader = new BufferedReader(new InputStreamReader(pIn));
while (true){
try {
line = reader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
engine.printConsollogOnGui(line);
}
}
}
I have the following short python program "test.py"
n = int(raw_input())
print n
I'm executing the above program from following java program "ProcessRunner.java"
import java.util.*;
import java.io.*;
public class ProcessRunner {
public static void main(String[] args) {
try {
Scanner s = new Scanner(Runtime.getRuntime().exec("python test.py").getInputStream()).useDelimiter("\\A");
System.out.println(s.next());
}
catch(Exception e) {
System.out.println(e.getMessage());
}
}
}
Upon running the command,
java ProcessRunner
I'm not able to pass a value 'n' in proper format to Python program and also the java run hangs. What is the proper way to handle the situation and pass a value to 'n' dynamically to python program from inside java program?
raw_input(), or input() in Python 3, will block waiting for new line terminated input on standard input, however, the Java program is not sending it anything.
Try writing to the Python subprocess using the stream returned by getOutputStream(). Here's an example:
import java.util.*;
import java.io.*;
public class ProcessRunner {
public static void main(String[] args) {
try {
Process p = Runtime.getRuntime().exec("python test.py");
Scanner s = new Scanner(p.getInputStream());
PrintWriter toChild = new PrintWriter(p.getOutputStream());
toChild.println("1234"); // write to child's stdin
toChild.close(); // or you can use toChild.flush()
System.out.println(s.next());
}
catch(Exception e) {
System.out.println(e.getMessage());
}
}
}
An alternative is to pass n as a command line argument. This requires modification of the Python script to expect and process the command line arguments, and to the Java code to send the argument:
import java.util.*;
import java.io.*;
public class ProcessRunner {
public static void main(String[] args) {
try {
int n = 1234;
Process p = Runtime.getRuntime().exec("python test.py " + n);
Scanner s = new Scanner(p.getInputStream());
System.out.println(s.next());
}
catch(Exception e) {
System.out.println(e.getMessage());
}
}
}
And the Python script, test.py:
import sys
if len(sys.argv) > 1:
print int(sys.argv[1])
If I understand you correctly you want your java program to pass any output from your python script to System.out and any input into your java program to your python Script, right?
Have a look at the following program to get an idea how you could do this.
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class ProcessRunner {
public static void main(String[] args) {
try {
final Process process = Runtime.getRuntime().exec("/bin/sh");
try (
final InputStream inputStream = process.getInputStream();
final InputStream errorStream = process.getErrorStream();
final OutputStream outputStream = process.getOutputStream()
) {
while (process.isAlive()) {
forwardOneByte(inputStream, System.out);
forwardOneByte(errorStream, System.err);
forwardOneByte(System.in, outputStream);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static void forwardOneByte(final InputStream inputStream,
final OutputStream outputStream)
throws IOException {
if(inputStream.available() <= 0) {
return;
}
final int b = inputStream.read();
if(b != -1) {
outputStream.write(b);
outputStream.flush();
}
}
}
Note This code is just a concept demo. It will eat up your cpu and will not be able to cope with bigger amounts of throughput.
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?
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");
}
}
(I'm attempting to make my previous question more generic in the hopes of a solution.)
I am using the JLayer library and a sample.mp3 file. I would like to play AND decode the file at the same time.
However, I want them to be synchronized - if a part of the song is decoded, it is also played. Nothing is decoded before it is played and vice versa (to a reasonable degree, of course).
Here is how a song is played and decoded, respectfully:
Player p = new Player(InputStream mp3stream);
p.play();
Decoder d = new Decoder();
BitStream bs = new Bitstream(InputStream mp3stream);
SampleBuffer s = (SampleBuffer) d.decodeFrame(bs.readFrame(), bs);
// ... for processing the SampleBuffer but irrelevant for the question
I currently use:
InputStream mp3stream = new FileInputStream("sample.mp3");
but this uses the whole song at once so I am unable to synchronize. Is there a way to break the sample.mp3 into pieces that can be manipulated by both processes? If I had small enough pieces I could run both pieces into the processes, wait until both finished, and then grab the next small piece and repeat until I was out of small pieces.
Note: I have tried using ByteArrayInputStream with no success - but perhaps my methodology is incorrect when using it.
I hope i get this right:
You have a single input file
You want that two different input streams are synchronized in the sense, that "they must make the same progress" in the stream.
This is an interestig question. I came up with the following sketch (compiles, but didn't execute it, so you may do a little testing first).
Create a wrapper object "StreamSynchronizer" that controls access to the underlying input. Only a single byte is read until all derived streams have read this byte.
Derive any number of "SynchronizedStream" instances from this that delegate the "read" back t the StreamSynchronizer.
package de.mit.stackoverflow;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class StreamSynchronizer {
final private InputStream inputStream;
private List activeStreams = new ArrayList();
private int lastByte;
private Set waitingStreams = new HashSet();
private Object lock = new Object();
public StreamSynchronizer(InputStream is) throws IOException {
super();
this.inputStream = is;
lastByte = getInputStream().read();
}
public void close(SynchronizedStream stream) {
activeStreams.remove(stream);
}
public SynchronizedStream createStream() {
SynchronizedStream stream = new SynchronizedStream(this);
activeStreams.add(stream);
return stream;
}
public InputStream getInputStream() {
return inputStream;
}
public int read(SynchronizedStream stream) throws IOException {
synchronized (lock) {
while (waitingStreams.contains(stream)) {
if (waitingStreams.size() == activeStreams.size()) {
waitingStreams.clear();
lastByte = getInputStream().read();
lock.notifyAll();
} else {
try {
lock.wait();
} catch (InterruptedException e) {
throw new IOException(e);
}
}
}
waitingStreams.add(stream);
return lastByte;
}
}
}
package de.mit.stackoverflow;
import java.io.IOException;
import java.io.InputStream;
public class SynchronizedStream extends InputStream {
final private StreamSynchronizer synchronizer;
protected SynchronizedStream(StreamSynchronizer synchronizer) {
this.synchronizer = synchronizer;
}
#Override
public void close() throws IOException {
getSynchronizer().close(this);
}
public StreamSynchronizer getSynchronizer() {
return synchronizer;
}
#Override
public int read() throws IOException {
return getSynchronizer().read(this);
}
}