单元测试 Bash 脚本

我们有一个除了 Java 代码之外还运行一些 Bash 脚本的系统。因为我们正在尝试 测试一切可能破坏的东西,并且那些 Bash 脚本可能会中断,所以我们想要测试它们。

问题是很难测试 Bash 脚本。

有没有测试 Bash 脚本的方法或最佳实践?或者我们应该停止使用 Bash 脚本,寻找可测试的替代解决方案?

99944 次浏览

实际上有一个 Shunit2,它是一个基于 xUnit 的单元测试框架,用于基于 Bourne 的 shell 脚本。我自己还没用过,不过可能值得一试。

以前也有人提出过类似的问题:

为什么说测试 Bash 脚本“很难”?

下面这样的测试包装器有什么问题吗?

 #!/bin/bash
set -e
errors=0
results=$($script_under_test $args<<ENDTSTDATA
# inputs
# go
# here
#
ENDTSTDATA
)
[ "$?" -ne 0 ] || {
echo "Test returned error code $?" 2>&1
let errors+=1
}


echo "$results" | grep -q $expected1 || {
echo "Test Failed.  Expected $expected1"
let errors+=1
}
# And so on, et cetera, ad infinitum, ad nauseum
[ "$errors" -gt 0 ] && {
echo "There were $errors errors found"
exit 1
}

我从一个讨论组得到了以下答案:

可以进口(包括, 过程(函数), 不管它叫什么名字)从一个外部 这是写一个 测试脚本: 将您的 将脚本转换为独立程序 然后可以导入到两者中 运行脚本和测试 然后运行 脚本尽可能简单。

对于脚本来说,这种方法类似于 依赖注入,而且听起来很合理。最好避免使用 Bash 脚本,并使用更易测试、更少晦涩的语言。

TAP -兼容 Bash 测试: < strong > 自动测试系统

TAP,Test AnyProtocol,是测试工具中测试模块之间的一个简单的基于文本的接口。TAP 最初是作为 Perl 测试工具的一部分出现的,但是现在已经有了 C、 C + + 、 Python、 PHP、 Perl、 Java、 JavaScript 和其他语言的实现。

Epoxy 是我主要为测试其他软件而设计的 Bash 测试框架,但我也使用它来测试 Bash 模块,包括 本身纸箱

主要优点是相对较低的编码开销、无限的断言嵌套和灵活选择要验证的断言。

我做了 演讲比较它与 烧杯解放-一个框架使用的一些在红帽。

我非常喜欢 Shell2junit,它是一个从 Bash 脚本测试生成类似 JUnit的输出的实用程序。这很有用,因为生成的报告可以被持续集成系统读取,例如 詹金斯竹子的 JUnit 插件。

虽然 shell2junit 没有提供像 Shunit2这样的全面的 Bash 脚本框架,但是它确实允许您对测试结果进行良好的报告。

您可能想看看 bash _ unit:

Https://github.com/pgrange/bash_unit

试试 最害羞的。这是测试脚本的简单方法。例如,您有 do-some-work.sh,它可以更改一些配置文件。例如,向配置文件 /etc/my.cfg添加一个新行 PASSWORD = 'XXXXX'

您逐行编写 Bash 命令,然后检查输出。

安装:

pip3 install bashtest

创建测试只是编写 bash 命令。

文件 test-do-some-work.bashtest:

# Run the script
$ ./do-some-work.sh > /dev/null


# Testing that the line "PASSWORD = 'XXXXX'" is in the file /etc/my.cfg
$ grep -Fxq "PASSWORD = 'XXXXX'" /etc/my.cfg && echo "YES"
YES

运行测试:

bashtest *.bashtest

你可以找到 这里有一些例子给你

也许这可以用来,或者有助于:

Https://thorsteinssonh.github.io/bash_test_tools/

它的目的是在 TAP 协议中编写结果,我想这对于 线人和那些想要 shell 环境的人来说是好的。我想象有些东西在 shell 环境中运行,因此有些人可能认为应该在他们的 shell 环境中进行测试。

试试 Assert.sh:

source "./assert.sh"


local expected actual
expected="Hello"
actual="World!"
assert_eq "$expected" "$actual" "not equivalent!"
# => x Hello == World :: not equivalent!

Nikita Sobolev 写了一篇精彩的博文,比较了几种不同的 Bash 测试框架: 测试 Bash 应用程序

对于不耐烦的人: Nikita 的结论是使用 蝙蝠,但是看起来 Nikita 错过了 蝙蝠核项目,在我看来这个项目应该继续使用,因为最初的 Bats 项目自2013年以来就没有积极维护过。

看看 外来的。它很简单,可以通过多种语言(选择 Perl巨蟒露比和 Bash)和跨平台(Linux 和 Windows)框架来测试任何命令行应用程序。

我真不敢相信竟然没人谈论 OSHT!它与 都有 水龙头JUnit兼容,它是纯 shell (也就是说,不涉及其他语言) ,它也是独立工作的,它简单而直接。

测试如下所示(摘自项目页面的片段) :

#!/bin/bash
. osht.sh


# Optionally, indicate number of tests to safeguard against abnormal exits
PLAN 13


# Comparing stuff
IS $(whoami) != root
var="foobar"
IS "$var" =~ foo
ISNT "$var" == foo


# test(1)-based tests
OK -f /etc/passwd
NOK -w /etc/passwd


# Running stuff
# Check exit code
RUNS true
NRUNS false


# Check stdio/stdout/stderr
RUNS echo -e 'foo\nbar\nbaz'
GREP bar
OGREP bar
NEGREP . # verify empty


# diff output
DIFF <<EOF
foo
bar
baz
EOF


# TODO and SKIP
TODO RUNS false
SKIP test $(uname -s) == Darwin

一个简单的步骤:

$ bash test.sh
1..13
ok 1 - IS $(whoami) != root
ok 2 - IS "$var" =~ foo
ok 3 - ISNT "$var" == foo
ok 4 - OK -f /etc/passwd
ok 5 - NOK -w /etc/passwd
ok 6 - RUNS true
ok 7 - NRUNS false
ok 8 - RUNS echo -e 'foo\nbar\nbaz'
ok 9 - GREP bar
ok 10 - OGREP bar
ok 11 - NEGREP . # verify empty
ok 12 - DIFF <<EOF
not ok 13 - TODO RUNS false # TODO Test Know to fail

最后一个测试显示为“ not ok”,但是退出代码为0,因为它是 TODO。人们也可以设置冗长的内容:

$ OSHT_VERBOSE=1 bash test.sh # Or -v
1..13
# dcsobral \!= root
ok 1 - IS $(whoami) != root
# foobar =\~ foo
ok 2 - IS "$var" =~ foo
# \! foobar == foo
ok 3 - ISNT "$var" == foo
# test -f /etc/passwd
ok 4 - OK -f /etc/passwd
# test \! -w /etc/passwd
ok 5 - NOK -w /etc/passwd
# RUNNING: true
# STATUS: 0
# STDIO <<EOM
# EOM
ok 6 - RUNS true
# RUNNING: false
# STATUS: 1
# STDIO <<EOM
# EOM
ok 7 - NRUNS false
# RUNNING: echo -e foo\\nbar\\nbaz
# STATUS: 0
# STDIO <<EOM
# foo
# bar
# baz
# EOM
ok 8 - RUNS echo -e 'foo\nbar\nbaz'
# grep -q bar
ok 9 - GREP bar
# grep -q bar
ok 10 - OGREP bar
# \! grep -q .
ok 11 - NEGREP . # verify empty
ok 12 - DIFF <<EOF
# RUNNING: false
# STATUS: 1
# STDIO <<EOM
# EOM
not ok 13 - TODO RUNS false # TODO Test Know to fail

将其重命名为使用 .t扩展名,并将其放在 t子目录中,您可以使用 prove(1)(Perl 的一部分)来运行它:

$ prove
t/test.t .. ok
All tests successful.
Files=1, Tests=13,  0 wallclock secs ( 0.03 usr  0.01 sys +  0.11 cusr  0.16 csys =  0.31 CPU)
Result: PASS

设置 OSHT_JUNIT或传递 -j以生成 JUnit 输出。 JUnit 也可以与 prove(1)组合。

我已经使用这个库来测试函数,方法是提供它们的文件,然后使用 IS/OK和它们的底片运行断言,使用 RUN/NRUN运行脚本。对我来说,这个框架以最小的开销提供了最大的收益。

我已经尝试了这里提供的许多解决方案,但是我发现它们中的大多数都过于庞大和难以使用,所以我构建了自己的小测试框架: https://github.com/SnacOverflow/t-bash

它只是存储库中的一个文件,您可以使用一组基本的 JUnit样式断言直接运行它。

我已经在几个内部项目中专业地使用过它,并且能够使我们的 Bash 脚本超级稳定和抗回归。

我创建了 贝壳规格,因为我想要一个易于使用和有用的工具。

它由纯 POSIX shell 脚本编写。它已经在许多炮弹上进行了测试,而不是 shunit2。它的功能比蝙蝠/蝙蝠核还要强大。

例如,它支持嵌套块,易于模拟/存根,易于跳过/挂起,参数化测试,断言行号,按行号执行,并行执行,随机执行,水龙头/JUnit格式化程序,覆盖率和 线人集成,分析器等。

请参见项目页面上的演示。

创建一个测试 mytest.sh。它使用特定的输入调用您的脚本。

创建一个文本文件 expected/mytest.stdout。它包含测试的预期输出。

将它们提交给版本控制(如果有的话)。

运行测试并将输出重定向到另一个文件:

mytest.sh > actual/mytest.stdout

然后只是我们的文字差异工具,看看哪里的结果偏离。我想你可以只做

diff expected/mytest.stdout actual/mytest.stdout