如何对命令行界面进行单元测试

5
我写了一个命令行工具,想要测试它(我不想从命令行运行单元测试)。我想将一组特定的输入选项映射到特定的输出。我没有找到任何现成的工具可以做到这一点。该应用程序只是一个二进制文件,可以用任何语言编写,但它接受POSIX选项并写入标准输出。
大致如下:
对于每个已知的输入选项集: 1. 使用指定的输入启动应用程序。 2. 将输出导入文件。 3. 将输出与所需输出进行比较。 4. 如果差异不为空,则记录错误。
(顺便问一下,这是称为“集成测试”而不是“单元测试”吗?)
编辑:我知道如何编写自己的工具,我不需要代码方面的帮助。我想了解的是是否已经有人做过这件事。

如果您决定实现它,就没有必要将其管道输出到文件。您可以直接重定向输出并使用字符串进行操作。这样会更快,并且避免在单元测试中使用更多的文件(这完全不建议)。 - Oscar Mederos
不知道有什么工具,但有几个建议:a)使用数据驱动测试来测试命令行输入的各种组合;b)添加一个步骤来检查工具的返回代码,这样你就不仅仅依赖于输出流数据。 - allen
@OscarMederos 也许你不建议使用文件,但我总是这样做。除非测试输出可预测地很小,在内存中使用字符串与通过现代基于缓存的文件系统访问磁盘驻留文件相比,是一个不好的选择。 - Ross Patterson
这是一个集成测试还是单元测试取决于测试的内容,但很可能是前者,因为您可能正在测试比单个代码单元更复杂的行为。 - Ross Patterson
@RossPatterson 我不是说你不能做。我只是在说你应该小心。在我上一个项目中,每次我用 MS Excel 打开一个 .csv 文件时,与该文件一起工作的测试都会失败,因为该文件已经被另一个进程使用了。 - Oscar Mederos
5个回答

6

DejaGnu是一个成熟且在CLI程序测试套件编写方面比较标准的框架。

以下是从这个教程中提取的样例测试:

# send a string to the running program being tested:
send "echo Hello world!\n"

# inspect the output and determine whether the test passes or fails:
expect {
    -re "Hello world.*$prompt $" {
        pass "Echo test"
    }
    -re "$prompt $" {
        fail "Echo test"
    }
    timeout {
        fail "(timeout) Echo test"
    }
}

使用像这样的既定框架,长期来看可能会比你自己想出的任何东西更好,除非你的需求非常简单。

4
你正在寻找 BATS (Bash 自动化测试系统): https://github.com/bats-core/bats-core 文档中介绍如下:
example.bats contains
#!/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 example.bats

 ✓ addition using bc
 ✓ addition using dc

2 tests, 0 failures

{{link1:蝙蝠核心}}


0

我认为每种编程语言都应该有一种执行外部进程的方法。

在C#中,你可以这样做:

var p = new Process();
p.StartInfo = new ProcessStartInfo(@"C:\file-to-execute.exe");
... //You can set parameters here, etc.
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.UseShellExecute = false;
p.Start();

//To read the standard output:
var output = p.StandardOutput.ReadToEnd();

我从未必须写标准输入,但我相信可以通过访问p.StandardInput来完成。这个想法是将两个输入都视为Stream对象,因为它们就是这样的。

在Python中有subprocess模块。根据其文档:

子进程模块允许您生成新进程,连接到其输入/输出/错误管道,并获取其返回代码。

几个月前,当我为编译器的代码生成部分编写单元测试时,我也不得不做同样的事情:在我的生成IL的编译器中编写单元测试


当我发布问题时,我也开始用Python编写工具。我知道如何解决这个问题,我想学习的是是否有其他人已经做过同样的事情。 - Mizipzor
@mizipzor 好吧,你问题中唯一包含真正问题的部分是:“如何对命令行界面进行单元测试”。如果你在寻找现有的工具或类似的东西,那么你应该编辑它。 - Oscar Mederos

0

我们编写了should,一个单文件Python程序,用于测试任何CLI工具。默认用法是检查输出的一行是否包含某个模式。从文档中可以看到:

# A .should file launches any command it encounters.
echo "hello, world"

# Lines containing a `:` are test lines.
# The `test expression` is what is found at the right of the `:`.
# Here 'world' should be found on stdout, at least in one line.
:world

# What is at the left of the `:` are modifiers.
# One can specify the exact number of lines where the test expression has to appear.
# 'moon' should not be found on stdout.
0:moon

应该能够检查出现次数,查找正则表达式,使用变量,过滤测试,解析JSON数据并检查退出代码。


-4
当然可以做到,这已经做了成千上万次。但是编写一个运行简单 shell 脚本或批处理文件的工具,像您所建议的那样,是一项微不足道的任务,几乎没有将其转化为通用工具的价值。

我肯定不是第一个希望有一个通用工具的人吧? - Mizipzor
1
xxx 2>&1 > results.txt & diff -q expected.txt result.txt 有多难? - Ross Patterson

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