如何测试命令行界面(CLI)?

9

我的Java应用程序由两部分组成:

  1. 核心库(类,接口等)
  2. 命令行界面(CLI),它使用核心库

对于第一部分,我使用JUnit进行单元测试,但是对于第二部分,你会怎么做呢?

如何为命令行界面创建自动化测试?

2个回答

12
我遇到了完全相同的问题,来到这里并没有找到一个好的答案,所以我想发布我最终得出的解决方案,作为未来任何人的起点。
我在CLI之后编写了我的测试(我知道这很不应该),因此首先我确保CLI是以可测试的方式编写的。它看起来像这样(我省略了异常处理并简化了很多内容,以使其更易读):
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)
    }
}

接下来是测试。 CLI 不属于公共库,因此应该受到库用户的保护。正如 这里 提到的那样,您可以使用默认访问修饰符将其设置为包私有。这使得您的测试可以完全访问该类(只要它们在同一个包中),同时仍然保护它,所以问题解决了。

为 CLI 接受的每个命令编写一个方法允许 JUnit 测试几乎完美地模拟用户输入。由于对象在调用 startInterface() 之前不会从 stdin 中读取数据,因此您可以简单地实例化它并测试各个方法。

首先,测试正确清理原始输入是否正确,您可以通过编写 sanitiseInput() 的 JUnit 测试轻松完成。我编写了以下测试:

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

覆盖一些边缘情况也很容易:

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

接下来,我们可以通过监控标准输出来测试单个命令方法。我在这里找到了一个方法(链接)

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

换句话说,在练习之前,用可以查询的东西替换JVM的out,然后在测试结束时将旧控制台设置回去。
当然,命令行应用程序通常不仅仅只是输出到控制台。假设您的程序保存信息到文件中,您可以按照以下方式进行测试(截至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将负责创建有效的文件,并在测试运行结束时将其删除,让您自由测试CLI方法是否正确处理它。

3
任何CLI都可以使用BATS(Bash自动化测试系统)
文档中的测试规范是一个脚本文件,例如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 ]
}

当使用bats命令执行时,输出结果如下:
$ bats example.bats

 ✓ addition using bc
 ✓ addition using dc

2 tests, 0 failures

查看相关标签以获取更多问题:


2
海报说他们的应用程序是用Java编写的。 - David Castillo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接