Issue
In testing a Java program with console inputs, I have created a ByteArrayOutputStream and set it as the system in for testing. First test executed correctly and output and input streams look good. However when the second test executes the input stream does not capture correctly and I am unsure why.
I have tried flushing and closing output streams in class constructors and destructors. Empty initializing input streams.
Main
import java.io.*;
public class TriTyp {
private static String[] triTypes = { "", // Ignore 0.
"scalene", "isosceles", "equilateral", "not a valid triangle"};
private static String instructions = "This is the ancient TriTyp program.\nEnter three integers that represent the lengths of the sides of a triangle.\nThe triangle will be categorized as either scalene, isosceles, equilateral\nor invalid.\n";
public static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
public static void main (String[] argv)
{ // Driver program for TriTyp
int A, B, C;
int T;
System.out.println (instructions);
System.out.println ("Enter side 1: ");
A = getN();
System.out.println ("Enter side 2: ");
B = getN();
System.out.println ("Enter side 3: ");
C = getN();
T = triang (A, B, C);
System.out.println ("Result is: " + triTypes[T]);
}
private static int triang (int Side1, int Side2, int Side3)
{
if (Side1 <= 0 || Side2 <= 0 || Side3 <= 0)
{
triOut = 4;
return (triOut);
}
triOut = 0;
if (Side1 == Side2)
triOut = triOut + 1;
if (Side1 == Side3)
triOut = triOut + 2;
if (Side2 == Side3)
triOut = triOut + 3;
if (triOut == 0)
{
if (Side1+Side2 <= Side3 || Side2+Side3 <= Side1 ||
Side1+Side3 <= Side2)
triOut = 4;
else
triOut = 1;
return (triOut);
}
if (triOut > 3)
triOut = 3;
else if (triOut == 1 && Side1+Side2 > Side3)
triOut = 2;
else if (triOut == 2 && Side1+Side3 > Side2)
triOut = 2;
else if (triOut == 3 && Side2+Side3 > Side1)
triOut = 2;
else
triOut = 4;
return (triOut);
}
private static int getN ()
{
int inputInt = 1;
String inStr;
try
{
inStr = in.readLine ();
inputInt = Integer.parseInt(inStr);
}
catch (IOException e)
{
System.out.println ("Could not read input, choosing 1.");
}
catch (NumberFormatException e)
{
System.out.println ("Entry must be a number, choosing 1.");
}
return (inputInt);
}
}
Test
import java.io.*;
import java.util.Arrays;
import static org.junit.Assert.*;
import org.junit.*;
import java.nio.charset.StandardCharsets;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
public class TriTypTest {
private PrintStream originalOut = System.out;
private PrintStream originalErr = System.err;
private InputStream originalIn = System.in;
private ByteArrayOutputStream outContent = new ByteArrayOutputStream();
@Before
public void setUp() throws Exception {
outContent = new ByteArrayOutputStream();
System.setOut(new PrintStream(outContent));
}
@After
public void tearDown() throws Exception {
System.out.flush();
System.setOut(originalOut);
}
@Test
public void TestEquilateral() {
// Set up input of an equilateral triangle for TriTyp.main()
// String mockinput = "3" + System.lineSeparator() + "3" + System.lineSeparator() + "3";
String mockinput = "3\r\n3\r\n3";
ByteArrayInputStream in = new ByteArrayInputStream(mockinput.getBytes());
// Set up mock user input stream
System.setIn(in);
// Initialize the TriTyp.main(input) method
String[] mainin = {};
TriTyp.main(mainin);
// Capture the standard output
String triout = outContent.toString();
String[] chopout = triout.split(System.lineSeparator());
String triangletype = chopout[chopout.length - 1];
// Call assert for equilateral triangle output
assertEquals("Result is: equilateral",triangletype);
}
@Test
public void TestScalene() throws Exception {
// Set up input of an equilateral triangle for TriTyp.main()
String mockinput = "3\r\n4\r\n2";
ByteArrayInputStream in = new ByteArrayInputStream(mockinput.getBytes());
// Set up mock user input stream
System.setIn(in);
// Initialize the TriTyp.main(input) method
String[] mainin = {};
TriTyp.main(mainin);
// Capture the standard output
String triout = outContent.toString();
String[] chopout = triout.split(System.lineSeparator());
String triangletype = chopout[chopout.length - 1];
// Call assert for equilateral triangle output
assertEquals("Result is: scalene", triangletype);
}
}
Last test assert fails:
org.junit.ComparisonFailure: Expected :Result is: scalene Actual :Result is: equilateral
Solution
Your problem is here:
public static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
In your test you create a new Reader with mock input and replace System.in
with it. Problem is that this static member is created only once and is not replaced by a new mock. This causes it to return null on subsequent reads which default to 1 causing the wrong output.
What I would suggest as fix would be to drop static, inject the reader so that it can be replaced and use main only as composition root.
public class TriTyp {
private static String[] triTypes = {"", // Ignore 0.
"scalene", "isosceles", "equilateral", "not a valid triangle"};
private static String instructions = "This is the ancient TriTyp program.\nEnter three integers that represent the lengths of the sides of a triangle.\nThe triangle will be categorized as either scalene, isosceles, equilateral\nor invalid.\n";
private final BufferedReader in;
public TriTyp(BufferedReader in) {
this.in = in;
}
public static void main(String[] argv) {
new TriTyp(new BufferedReader(new InputStreamReader(System.in))).run();
}
public void run() {
int A, B, C;
int T;
System.out.println(instructions);
System.out.println("Enter side 1: ");
A = getN();
System.out.println("Enter side 2: ");
B = getN();
System.out.println("Enter side 3: ");
C = getN();
T = triang(A, B, C);
System.out.println("Result is: " + triTypes[T]);
}
// Triang and getN methods. Just drop their static keyword.
}
And in tests you would replace this:
String mockinput = "3\r\n3\r\n3";
ByteArrayInputStream in = new ByteArrayInputStream(mockinput.getBytes());
// Set up mock user input stream
System.setIn(in);
// Initialize the TriTyp.main(input) method
String[] mainin = {};
TriTyp.main(mainin);
With this:
String mockinput = "3\r\n3\r\n3";
ByteArrayInputStream in = new ByteArrayInputStream(mockinput.getBytes());
new TriTyp(new BufferedReader(new InputStreamReader(in))).run();
Answered By - Januson
Answer Checked By - Mary Flores (JavaFixing Volunteer)