JustWorkAlready
JustWorkAlready

Reputation: 77

Got user input from a for loop in a JUnit test, now how to actually test its output?

So suppose I have the following class with methodA():

public class example {

public void methodA() {
    System.out.println("Enter in an integer for each step.\n");
    Scanner scan = new Scanner(System.in); 
    int currentStepCount = 0; 
    int totalStepCount = 0;

    for (int step = 10; step <= 16; step++) {
        if (step == 15)
            continue; // Skip fifteenth step

        System.out.println("\nStep " + step + ": ");
        currentStepCount = scan.nextInt();

        totalStepCount += currentStepCount;
    }

    System.out.println("\n\nAll steps have been taken in.\n");
    System.out.println("Total step count comes out to: " + totalStepCount + "\n"); 
    System.out.println("The last step count was " + currentStepCount + ". \n");
}
}

And I have a JUnit test that's able to simulate the input:

class junitTests {
private final ByteArrayOutputStream output = new ByteArrayOutputStream();

@Before
public void setUpStreams() {
    System.setOut(new PrintStream(output));
}
@Test
void testA() {
    example ex = new example();
    ByteArrayInputStream in1 = new ByteArrayInputStream("13\n13\n13\n13\n13\n13\n".getBytes());

    System.setIn(in1);
    ex.methodA();
    String out = output.toString(); 

    System.out.println(out); //This isn't in the final unit test implementation, 
                            //just for demonstrating my issue.
}
}

but I'm stuck on how to actually test whether the output is the correct output. When I try to print ByteArrayOutputStream output it comes out as empty so when I try to do an assertEquals method, it always fails. How can actually make it so that output is not always empty. Or, more generally, how can I actually test the output of the method altogether. Thank you in advance!

Upvotes: 1

Views: 946

Answers (3)

Hardy
Hardy

Reputation: 477

I think the best long term solution to the problem is to use dependency injection. boot-and-bonnet is right that you aren't seeing the output because you overwrote the System.out stream. What you could do to avoid the need to override the System.out stream is to modify the method so you can pass in a stream you can write to.

public static void methodA(PrintStream out) {
    out.println("Enter in an integer for each step.\n");
    Scanner scan = new Scanner(System.in);
    int currentStepCount = 0;
    int totalStepCount = 0;

    for (int step = 10; step <= 16; step++) {
        if (step == 15)
            continue; // Skip fifteenth step

        out.println("\nStep " + step + ": ");
        currentStepCount = scan.nextInt();

        totalStepCount += currentStepCount;
    }

    out.println("\n\nAll steps have been taken in.\n");
    out.println("Total step count comes out to: " + totalStepCount + "\n");
    out.println("The last step count was " + currentStepCount + ". \n");
}

Then you call methodA like this

ByteArrayOutputStream output = new ByteArrayOutputStream();
PrintStream out = new PrintStream(output);
methodA(out);

And now you can do this

System.out.println(output.toString());

You can also run any assertions you need on that ByteArrayOutputStream.

Finally, in your code when you run the method outside a unit test you would call it like this

methodA(System.out);

Upvotes: 0

boot-and-bonnet
boot-and-bonnet

Reputation: 761

The last System.out.println is still using your replaced PrintStream, so your output byte array contains two copies. You could store the original System.out and use that to print to the console.

    private final ByteArrayOutputStream output = new ByteArrayOutputStream();
    private final PrintStream stdout = System.out;
    private final InputStream stdin = System.in;

    @Before
    public void setUpStreams() {
        System.setOut(new PrintStream(output));
    }

    @After
    public void restoreStreams() {
        System.setIn(stdin);
        System.setOut(stdout);
    }

    @Test
    public void testA() {
        example ex = new example();
        ByteArrayInputStream in1 = new ByteArrayInputStream("13\n13\n13\n13\n13\n13\n".getBytes());
        System.setIn(in1);
        ex.methodA();
        String out = output.toString();
        stdout.println(out);
    }

Upvotes: 1

kaya3
kaya3

Reputation: 51152

Since this is a JUnit test, you should test the output by making some assertion about it; e.g.

assertTrue(output.contains("Total step count comes out to: 15"));

if 15 is the correct result. However, this is a bad way of writing a test, because if it fails then you don't know what the actual step count was (instead of 15).

Better to write your method in a way which returns the step count (or whatever you want to test), and then use assertEquals to make the assertion about its value. This way you won't need to redirect System.out to test the results. Better still, write your method to take the numbers from an array so you don't need to redirect System.in either.

That said, if you just want to see the printed output of the method then why bother redirecting System.out in the first place? You could just let the method print what it prints.

Upvotes: 1

Related Questions