How to test a print method in Java using Junit [duplicate] - java

This question already has answers here:
JUnit test for System.out.println()
(14 answers)
Closed 6 years ago.
I have written a method that is printing output to a console. How should I test it?
public class PrinterForConsole implements Printer<Item>{
public void printResult(List<Item> items) {
for (Item item: items){
System.out.println("Name: " + item.getName());
System.out.println("Number: " + item.getNumber());
}
}
}
currently, my test looks like this
public class TestPrinter{
#Test
public void printResultTest() throws Exception {
(am figuring out what to put here)
}
}
I have read the solution at this post (thanks #Codebender and #KDM for highlighting this) but don't quite understand it. How does the solution there test the print(List items) method? Hence, asking it afresh here.

Since you have put you don't get what the duplicate question says, I will try to explain a little.
When you do, System.setOut(OutputStream), whatever the application writes to the console (using System.out.printX()) statements, instead get written to the outputStream you pass.
So, you can do something like,
public void printTest() throws Exception {
ByteArrayOutputStream outContent = new ByteArrayOutputStream();
System.setOut(new PrintStream(outContent));
// After this all System.out.println() statements will come to outContent stream.
// So, you can normally call,
print(items); // I will assume items is already initialized properly.
//Now you have to validate the output. Let's say items had 1 element.
// With name as FirstElement and number as 1.
String expectedOutput = "Name: FirstElement\nNumber: 1" // Notice the \n for new line.
// Do the actual assertion.
assertEquals(expectedOutput, outContent.toString());
}

The best way to test it is by refactoring it to accept a PrintStream as a parameter and you can pass another PrintStream constructed out of ByteArrayOutputStream and check what is printed into the baos.
Otherwise, you can use System.setOut to set your standard output to another stream. You can verify what is written into it after the method returns.
A simplified version with comments is below:
#Test
public void printTest() throws Exception {
// Create our test list of items
ArrayList<Item> items = new ArrayList<Item>();
items.add(new Item("KDM", 1810));
items.add(new Item("Roy", 2010));
// Keep current System.out with us
PrintStream oldOut = System.out;
// Create a ByteArrayOutputStream so that we can get the output
// from the call to print
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// Change System.out to point out to our stream
System.setOut(new PrintStream(baos));
print(items);
// Reset the System.out
System.setOut(oldOut);
// Our baos has the content from the print statement
String output = new String(baos.toByteArray());
// Add some assertions out output
assertTrue(output.contains("Name: KDM"));
assertTrue(output.contains("Name: Roy"));
System.out.println(output);
}
Note that if the print method throws an exception, the System.out is not reset. It is better to use setup and teardown methods to set and reset this.

How about something like this.
#Test
public void printTest() throws Exception {
OutputStream os = new ByteArrayOutputStream();
System.setOut(os);
objectInTest.print(items);
String actualOutput = os.toString("UTF-8");
assertEquals(expectedOutput, actualOutput);
}

This is the Simple Test Code :-
#Test
public void out() {
System.out.print("hello");
assertEquals("helloworld", outContent.toString());
}
#Test
public void err() {
System.err.print("helloworld 1 ");
assertEquals("helloworld 1", errContent.toString());
}
For more :JUnit test for System.out.println()

Eventually, what I came up with is this (after going through all the answers and links to possible duplicates above).
import org.junit.Test;
#Test
public void shouldPrintToConsole() throws Exception {
Item testItem = new Item("Name", "Number");
List<Item> items = Arrays.asList(testItem);
Printer print = new Printer();
printer.printOutput(items);
}
Read up on naming convention (shouldPrintToConsole()) for testing too. Wondering if this is the convention because I see many sites that follow and many that don't.

Related

JUnit Passing No arguments to main Method Java

I'm trying to test my main method (which should accept exactly one argument) for no arguments passed. Can't seem to understand what am I missing here to achieve that.The nature of my program is such that it reads input from a file, creates objects by passing parameters read from the file, and displays output.
Failure Message:
org.junit.ComparisonFailure: Expected :Please pass one argument
Actual :
Here's my Unit Test:
#Test
public void givenNoParameter_shouldAskForOne() throws IOException {
String[] args = {};
String output;
try (ByteArrayOutputStream bOutput = new ByteArrayOutputStream()) {
System.setOut(new PrintStream(bOutput));
Main.main(args);
bOutput.flush();
output = bOutput.toString();
}
String newLine = System.getProperty("line.separator");
String[] breakDownOutput = output.split(newLine);
assertEquals(1, breakDownOutput.length);
assertEquals("Please pass one argument", breakDownOutput[0]);
}
Main Method:
public static void main(String[] args) {
if(args.length == 1) {
DisplayOrder.setFilePath(args[0]);
DisplayOrder.display();
} else{
System.err.println("Please pass one argument");
}
}
I've realized I was using System.err.println() in my main. Changing that to System.out.println() fixed it.
Not showing your main method, my only guess is, that you do not write anything to "System.out" in your main, especially there is no System.out.println("Please pass one argument"); statement which is executed.
So, your unit test fails perfectly for a not expected value in "breakDownOutput[0]".
What you have to do is to make certain that the System.out.println("Please pass one argument"); is executed if no arguments were provided to your main.
Also check your class name Main.main(...) since there might be other Main classes imported which will never print out your expected values to System.out

How to make JUnit test for void method with Scanner and while loop [duplicate]

This question already has answers here:
JUnit test for System.out.println()
(14 answers)
Closed 4 years ago.
I want to test method setDay() using arguments [-1,0,24,2,32].
I know that Scanner reader can be
String test="-1 0 24 2 32";
Scanner reader=new Scanner(test);
The main problem is infinite loop and void method. How can we test this kind of code? Here is example code:
public NameOfTheDay(){
int day=1;
}
{...}
public void setDay(Scanner reader) {
while (true) {
System.out.print("Day: ");
String input = reader.nextLine();
if (input.matches("\\d{2}")) {
int day = Integer.parseInt(input);
if (day > 0 && day < 32) {
this.day = day;
return;
}
}
System.out.println("Wrong day. Try again.");
}
}
Thanks for the answer.
How can we test this kind of code?
You cannot.
unittest verify the public observable behavior of your code under test where "public observable behavior" is any rreturn value or communication with dependencies.
Communication with dependencies is checked with test doubles which we (usually) create using a mocking framework and which we inject into the code under test.
A major prerequest is that you code cleanly incorporates Single Responsibility/Separation of Concerns pattern.
Your code does not return anything and has no possibility to replace the dependencies of interest (here System.out) because it mixes business logic with user interaction.
Some may argue, that you can assign a test double to System.out or use PowerMock to replace dependencies but IMHO this is just a surrender to your bad design and will not pay off as your program grows.
I will not focus on the contents of your method, but just on the question on how to unit test a method expecting a Scanner object as parameter.
The easy answer is: Provide your test input data as a String, and build a scanner around it, like this:
#Test
public void testSetDay_positive() {
String testInput = "23\n";
Scanner testScanner = new Scanner(testInput);
NameOfTheDay notd = new NameOfTheDay();
notd.setDay(testScanner);
Assert.assertEquals(23, notd.getDay()); // or whatever condition to test
}
Now it gets harder. Perhaps you want to test an invalid input first, and make sure the second input is used then?
#Test
public void testSetDay_negative_then_positive() {
String testInput = "999\n23\n"; // two lines of input here
Scanner testScanner = new Scanner(testInput);
NameOfTheDay notd = new NameOfTheDay();
notd.setDay(testScanner);
Assert.assertEquals(23, notd.getDay()); // or whatever condition to test
}
If you want to test if the error message is written to System.out, you would have to replace that with a custom stream to test afterwards:
ByteArrayOutputStream mockOut = new ByteArrayOutputStream();
PrintStream newOut = new PrintStream(mockOut);
System.setOut(newOut);
// execute test from above
Assert.assertTrue(new String(mockOut.toByteArray(), "UTF-8").contains("Wrong day. Try again."));
Still, most comments to your question contain valuable input (move validation to an extra method etc.) which should be considered.

How to test a Command Line Interface (CLI)?

My Java application consists of two parts:
core libraries (classes, interfaces, etc)
command line interface (CLI), which uses the core libraries
For 1. I use JUnit for unit testing, but what would you do for 2.?
How can I create automated tests for a command line interface?
I had the exact same problem, landed here and didn't find a good answer, so I thought I would post the solution I eventually came to as a starting point for anyone who lands here in the future.
I wrote my tests after the CLI (shame on me, I know), so first I made sure the CLI was written in a testable way. It looks something like this (I've omitted the exception handling and simplified a lot to make it more readable):
public class CLI {
public static void main(String... args) {
new CLI(args).startInterface();
}
CLI(String... args) {
System.out.println("Welcome to the CLI!");
// parse args, load resources, etc
}
void startInterface() {
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String[] input = sanitiseInput(consoleReader.readLine());
if (input[0].equalsIgnoreCase("help") {
help();
} else if (input[0].equalsIgnoreCase("exit") {
break;
} else if (input[0].equalsIgnoreCase("save") {
save(input);
} else {
System.out.println("Unkown command.");
}
}
}
String[] sanitiseInput(String rawInput) {
// process the input and return each part of it in order in an array, something like:
return rawInput.trim().split("[ \t]+");
}
void help() {
// print help information
System.out.println("Helpful help.");
}
void save(String[] args) {
// save something based on the argument(s)
}
}
On to testing. CLI is not a part of the public libraries, so it should be protected from library users. As is mentioned here, you can use the default access modifier to make it package private. This gives your tests full access to the class (as long as they are in the same package) while still protecting it, so that's that taken care of.
Writing a method for each command accepted by the CLI allows JUnit tests to almost perfectly simulate user input. Since the object won't read from stdin until you call startInterface(), you can simply instantiate it and test the individual methods.
First, it's good to test that the raw input is being correctly sanitised, which you can do trivially by writing JUnit tests for sanitiseInput(). I wrote tests like this:
#Test
public void commandAndArgumentsSeparatedBySpaces() throws Exception {
String[] processedInput = uut.sanitiseInput("command argument1 argument2");
assertEquals("Wrong array length.", 3, processedInput.length);
assertEquals("command", processedInput[0]);
assertEquals("argument1", processedInput[1]);
assertEquals("argument2", processedInput[2]);
}
It's easy to cover some edge cases too:
#Test
public void leadingTrailingAndIntermediaryWhiteSpace() throws Exception {
String[] processedInput = uut.sanitiseInput(" \t this \twas \t \t a triumph \t\t ");
assertEquals("Wrong array length.", 4, processedInput.length);
assertEquals("this", processedInput[0]);
assertEquals("was", processedInput[1]);
assertEquals("a", processedInput[2]);
assertEquals("triumph", processedInput[3]);
}
Next we can test the invididual command methods by monitoring stdout. I did this (which I found here):
private CLI uut;
private ByteArrayOutputStream testOutput;
private PrintStream console = System.out;
private static final String EOL = System.getProperty("line.separator");
#Before
public void setUp() throws Exception {
uut = new CLI();
testOutput = new ByteArrayOutputStream();
}
#Test
public void helpIsPrintedToStdout() throws Exception {
try {
System.setOut(new PrintStream(testOutput));
uut.help();
} finally {
System.setOut(console);
}
assertEquals("Helpful help." + EOL, testOutput.toString());
}
In other words, substitute the JVM's out with something you can query just before the exercise, and then set the old console back in the test's teardown.
Of course, CLI applications often do more than just print to the console. Supposing your program saves information to a file, you could test it as such (as of JUnit 4.7):
#Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
#Test
public void informationIsSavedToFile() throws Exception {
File testFile = tempFolder.newFile();
String expectedContent = "This should be written to the file.";
uut.save(testFile.getAbsolutePath(), expectedContent);
try (Scanner scanner = new Scanner(testFile)) {
String actualContent = scanner.useDelimiter("\\Z").next();
assertEquals(actualContent, expectedContent);
}
}
JUnit will take care of creating a valid file and removing it at the end of the test run, leaving you free to test that it is properly treated by the CLI methods.
For any CLI you can use BATS (Bash Automated Testing System):
The test-specification from the docs is a script-file like example.bats:
#!/usr/bin/env bats
#test "addition using bc" {
result="$(echo 2+2 | bc)"
[ "$result" -eq 4 ]
}
#test "addition using dc" {
result="$(echo 2 2+p | dc)"
[ "$result" -eq 4 ]
}
When using the bats command to execute and the output look like this:
$ bats example.bats
✓ addition using bc
✓ addition using dc
2 tests, 0 failures
See related tag for more questions: bats-core

Getting an InputStream to read more than once, regardless of markSupported()

I need to be able to re-use a java.io.InputStream multiple times, and I figured the following code would work, but it only works the first time.
Code
public class Clazz
{
private java.io.InputStream dbInputStream, firstDBInputStream;
private ArrayTable db;
public Clazz(java.io.InputStream defDB)
{
this.firstDBInputStream = defDB;
this.dbInputStream = defDB;
if (db == null)
throw new java.io.FileNotFoundException("Could not find the database at " + db);
if (dbInputStream.markSupported())
dbInputStream.mark(Integer.MAX_VALUE);
loadDatabaseToArrayTable();
}
public final void loadDatabaseToArrayTable() throws java.io.IOException
{
this.dbInputStream = firstDBInputStream;
if (dbInputStream.markSupported())
dbInputStream.reset();
java.util.Scanner fileScanner = new java.util.Scanner(dbInputStream);
String CSV = "";
for (int i = 0; fileScanner.hasNextLine(); i++)
CSV += fileScanner.nextLine() + "\n";
db = ArrayTable.createArrayTableFromCSV(CSV);
}
public void reloadDatabase()//A method called by the UI
{
try
{
loadDatabaseToArrayTable();
}
catch (Throwable t)
{
//Alert the user that an error has occurred
}
}
}
Note that ArrayTable is a class of mine, which uses arrays to give an interface for working with tables.
Question
In this program, the database is shown directly to the user immediately after the reloadDatabase() method is called, and so any solution involving saving the initial read to an object in memory is useless, as that will NOT refresh the data (think of it like a browser; when you press "Refresh", you want it to fetch the information again, not just display the information it fetched the first time). How can I read a java.io.InputStream more than once?
You can't necessarily read an InputStream more than once. Some implementations support it, some don't. What you are doing is checking the markSupported method, which is indeed an indicator if you can read the same stream twice, but then you are ignoring the result. You have to call that method to see if you can read the stream twice, and if you can't, make other arrangements.
Edit (in response to comment): When I wrote my answer, my "other arrangements" was to get a fresh InputStream. However, when I read in your comments to your question about what you want to do, I'm not sure it is possible. For the basics of the operation, you probably want RandomAccessFile (at least that would be my first guess, and if it worked, that would be the easiest) - however you will have file access issues. You have an application actively writing to a file, and another reading that file, you will have problems - exactly which problems will depend on the OS, so whatever solution would require more testing. I suggest a separate question on SO that hits on that point, and someone who has tried that out can perhaps give you more insight.
you never mark the stream to be reset
public Clazz(java.io.InputStream defDB)
{
firstDBInputStream = defDB.markSupported()?defDB:new BufferedInputStream(defDB);
//BufferedInputStream supports marking
firstDBInputStream.mark(500000);//avoid IOException on first reset
}
public final void loadDatabaseToArrayTable() throws java.io.IOException
{
this.dbInputStream = firstDBInputStream;
dbInputStream.reset();
dbInputStream.mark(500000);//or however long the data is
java.util.Scanner fileScanner = new java.util.Scanner(dbInputStream);
StringBuilder CSV = "";//StringBuilder is more efficient in a loop
while(fileScanner.hasNextLine())
CSV.append(fileScanner.nextLine()).append("\n");
db = ArrayTable.createArrayTableFromCSV(CSV.toString());
}
however you could instead keep a copy of the original ArrayTable and copy that when you need to (or even the created string to rebuild it)
this code creates the string and caches it so you can safely discard the inputstreams and just use readCSV to build the ArrayTable
private String readCSV=null;
public final void loadDatabaseToArrayTable() throws java.io.IOException
{
if(readCSV==null){
this.dbInputStream = firstDBInputStream;
java.util.Scanner fileScanner = new java.util.Scanner(dbInputStream);
StringBuilder CSV = "";//StringBuilder is more efficient in a loop
while(fileScanner.hasNextLine())
CSV.append(fileScanner.nextLine()).append("\n");
readCSV=CSV.toString();
fileScanner.close();
}
db = ArrayTable.createArrayTableFromCSV(readCSV);
}
however if you want new information you'll need to create a new stream to read from again

Hide Java Output

I am using an external library. When a call a method of this library then it outputs some text on console. I want to hide this text from console. How is it possible?
Thanks in Advance
You can redefine system.out (I believe it is System.setOut()) I believe you can set it to NULL (Corrected--you can NOT set it to NULL), but you can set it to ANY output stream.
I did something interesting with this once. I saved "System.out" then redirected it to my own output stream with code in the "print" method--that method is called whenever anyone prints to the stream.
Every time a line of input came in to this class, I'd create a stack trace, grab the trace and dig down to the method that called the System.out.println() method. At this point I could prepend the line and have instant logger functionality--it even shows the line number.
This was probably quite slow but it could be turned on and off very easily.
I could also do all the filtering you'd like without touching the source code. Sometimes I'd filter on a single package, sometimes I'd filter on a class and sometimes it would be strings starting with "BK:" which would only print out my messages.
Overall it was a lot of fun and trivial to remove ALL debug output for production.
(I do not recommend the get stack trace thing for production code, it really should be quite slow even though I didn't notice it)
// class variable
public static final OutputStream out;
{
out=System.getOutputStream();// I may have the actual name wrong, but it's close
System.setOutputStream(new OutputStreamThatDoesNothing());
}
at this point any calls to:
Redirect.out("Hello");
Should act just as calls to System.out did.
Since there is no magic with this, you can also do something like this if you really want to:
OutputStream tmp=System.getOutputStream();
System.setOutpuatStream(nullStream);
callOffensiveLibraryMethod();
System.setOutputStream(tmp);
This would only eliminate output within that call BUT would be Very Bad if your application was multi-threaded.
private PrintStream realSystemOut = System.out;
private static class NullOutputStream extends OutputStream {
#Override
public void write(int b){
return;
}
#Override
public void write(byte[] b){
return;
}
#Override
public void write(byte[] b, int off, int len){
return;
}
public NullOutputStream(){
}
}
void someMethod(){
System.setOut(new PrintStream(new NullOutputStream());
realSystemOut.println("Hello World!"); //prints Hello World!
System.out.println("Hello World!"); //nothing!
System.setOut(realSystemOut);
System.out.println("Hello World!"); //prints Hello World!
}
For those looking for a 2020 version that also does not require external libraries for the null stream:
PrintStream out = System.out;
System.setOut(new PrintStream(OutputStream.nullOutputStream()));
//
//do stuff with library...
//
System.setOut(out);
Available since Java 11.
Set the System.out to a NullOutputStream. apacahe.commons has one available.
If you want to print it out accordingly you could write a fun hack.
private static final PrintStream SYSTEM_OUT = System.out();
public static void debug(String debug){
SYSTEM_OUT.println(debug);
}
Somehwere else in the code
System.setOut(new PrintStream(new NullOutputStream()));

Categories

Resources