如何使用 JUnit 在 Java 中测试简单的命令行应用程序

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

我如何使用 JUnit 测试我的简单主方法命令行 class,它使用 Scanner 从 system.in 读取并打印到 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());
    }
}

这是一个非常具体的用例,我正在做一门数据结构和算法课程,要求参与者上传一个 *.java 文件,其中的主要方法是从 System.in 读取(使用 Scanner) 并输出 System.out 的答案。当您上传代码时,网站会构建并运行应用程序,并测试和衡量算法的性能。

我想要一种模拟这种情况的方法,这样我就可以使用 JUnit 编写简单的集成测试,作为对每个任务采用 TDD 方法的一部分。其他堆栈溢出线程上有一些有用的指针可以提供帮助,但没有满足全部要求。

为了解决这个问题,我创建了一个私有方法,我可以从中传递命令行输入和主要方法的名称 class 我想用反射调用。然后我可以将标准的 hamcrest macthers 应用于输出。

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);
        }
    }
}

在实际实施中,我还测量了执行代码所需的时间,并且能够设置阈值限制以进行测试。