How to test a simple command line application in Java using JUnit

4.8k views Asked by At

How can I JUnit test my simple main method command line class which reads from system.in using Scanner and prints to system.out.

import java.util.Scanner;

public class SimpleMainCmdApplication {

    public static void main(String args[]) {
        Scanner scanner = new Scanner(System.in);
        System.out.println(scanner.next());
    }
}

This is quite a specific use case where I am doing a course data structures and algorithms course which requires participants to upload a *.java file with the main method which reads from System.in (using Scanner) and outputs answers to System.out. When you upload your code the website builds and runs the application and tests and measures the performance of your algorithm.

I wanted a way to simulate this so I can write simple integration tests using JUnit as part of taking a TDD approach to each assignment. There were some useful pointers on other stack overflow threads which helped, but nothing which met the full requirements.

1

There are 1 answers

1
Justin.Cooke On BEST ANSWER

To solve this I created a private method from which I can pass the command line inputs and the name of the main method class I want to invoke with reflection. I can then apply standard hamcrest macthers to the output.

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;

import org.junit.Before;
import org.junit.Test;

public class MainCmdTest {

    private ByteArrayOutputStream byteArrayOutputStream;
    private PrintStream console;

    @Before
    public void setup() {
        byteArrayOutputStream = new ByteArrayOutputStream();
        console = System.out;
    }

    @Test
    public void shouldPrintTest() throws Exception {
        runTest("test", "SimpleMainCmdApplication");
        assertThat(byteArrayOutputStream.toString(), is("test\n"));
    }

    private void runTest(final String data, final String className) throws Exception {

        final InputStream input = new ByteArrayInputStream(data.getBytes("UTF-8"));
        final InputStream old = System.in;

        try {
            System.setOut(new PrintStream(byteArrayOutputStream));
            System.setIn(input);

            final Class<?> cls = Class.forName(className);
            final Method meth = cls.getDeclaredMethod("main", String[].class);
            final String[] params = new String[]{};
            meth.invoke(null, (Object) params);

        } finally {
            System.setOut(console);
            System.setIn(old);
        }
    }
}

In the actual implementation I also measure the time it takes to execute the code and am able to set threshold limits to test against.