我们有一个系统除了Java代码外还有一些运行bash脚本。由于我们正在尝试测试可能会破坏的所有内容,并且这些bash脚本可能会中断,我们希望对它们进行测试。
问题是很难测试bash脚本。
有没有办法或最佳做法来测试bash脚本?或者我们应该退出使用bash脚本并寻找可测试的替代解决方案吗?
答案 0 :(得分:42)
实际上有一个shunit2,一个基于xUnit的单元测试框架,用于基于Bourne的shell脚本。我自己没有用过,但可能值得一试。
以前曾提出过类似的问题:
答案 1 :(得分:25)
我从讨论组得到了以下答案:
可以导入(包括, 无论如何)一个程序(功能, 无论它的名字来自外部 文件。这是写作的关键 测试脚本:你分手了 脚本进入独立程序 然后可以导入到两者中 你的运行脚本和你的测试 脚本,然后你运行 脚本尽可能简单。
这种方法就像脚本的依赖注入一样,听起来很合理。最好避免使用bash脚本并使用更易测试且不那么模糊的语言。
答案 2 :(得分:23)
TAP - 符合Bash测试: Bash Automated Testing System
测试任务协议TAP是测试工具中测试模块之间的简单基于文本的界面。 TAP最初是Perl测试工具的一部分,但现在有C,C ++,Python,PHP,Perl,Java,JavaScript等实现。
答案 3 :(得分:7)
Epoxy是我设计的Bash测试框架,主要用于测试其他软件,但我也用它来测试bash模块,包括itself和Carton。
主要优点是编码开销相对较低,无限制断言嵌套以及灵活选择断言以进行验证。
我将presentation与BeakerLib进行了比较 - 这是红帽的一些人使用的框架。
答案 4 :(得分:6)
为什么你说测试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
}
答案 5 :(得分:5)
Nikita Sobolev撰写了一篇精彩的博客文章,比较了一些不同的bash测试框架:Testing Bash applications
对于不耐烦的人:Nikita的结论是使用Bats但是看起来Nikita错过了Bats-core项目,在我看来这是一个可以使用的项目,因为原来的Bats项目还没有自2013年以来积极维护。
答案 6 :(得分:3)
我非常喜欢shell2junit,一个从Bash脚本测试生成类似JUnit的输出的实用程序。这很有用,因为生成的报告可以由持续集成系统读取,例如Jenkins和Bamboo的JUnit插件。
虽然shell2junit不提供像shunit2这样全面的Bash脚本框架,但它确实可以很好地报告测试结果。
答案 7 :(得分:3)
试试bashtest。这是测试脚本的简单方法。例如,您有do-some-work.sh
更改了一些配置文件。例如,将新行PASSWORD = 'XXXXX'
添加到配置文件/etc/my.cfg
。
您逐行编写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
您可以找到some examples here和here
答案 8 :(得分:3)
也许这可以用于
https://thorsteinssonh.github.io/bash_test_tools/
打算在TAP协议中写入结果,我认为它对CI有好处,对那些想要shell环境的人有好处。我想有些东西在shell环境中运行,所以有些人可能认为应该在shell环境中进行测试。
答案 9 :(得分:3)
source "./assert.sh"
local expected actual
expected="Hello"
actual="World!"
assert_eq "$expected" "$actual" "not equivalent!"
# => x Hello == World :: not equivalent!
希望它有所帮助!
答案 10 :(得分:1)
我创建了shellspec是因为我想要一个易于使用且有用的工具。
它是由纯POSIX shell脚本编写的。它比shunit2对许多外壳进行了更多测试。它具有比bats / bats-core强大的功能。
例如,支持嵌套块,易于模拟/存根,易于跳过/挂起,声明行号,按行号执行,并行执行,随机执行,TAP / JUnit格式化程序,覆盖率和CI集成,探查器等
请参阅项目页面上的演示。
答案 11 :(得分:1)
我不敢相信没有人谈论OSHT!它与 TAP和JUnit都兼容,它是纯外壳程序(也就是说,不涉及其他语言),它也可以独立工作,并且简单直接。
测试看起来像这样(摘录自项目页面):
#!/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
最后一个测试显示为“不正常”,但是退出代码为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
来运行脚本。对我来说,此框架以最少的开销提供了最大的收益。
答案 12 :(得分:0)
创建一个测试 mytest.sh
。它使用特定输入调用您的脚本。
创建一个文本文件 expected/mytest.stdout
。它包含测试的预期输出。
将它们提交到版本控制(如果有)。
运行测试并将输出重定向到另一个文件:
mytest.sh > actual/mytest.stdout
然后只是我们一个文本差异工具来查看结果偏离的地方。我认为你可以这样做
diff expected/mytest.stdout actual/mytest.stdout
答案 13 :(得分:0)
我尝试了此处介绍的许多解决方案,但是发现其中大多数解决方案体积庞大且难以使用,因此我建立了自己的小型测试框架:https://github.com/meonlol/t-bash
只有一个基本的JUnit样式断言,您可以直接运行该仓库中的一个文件。
我已经在多个内部项目中专业地使用过它,并且能够使我们的bash脚本具有超强的稳定性和抗回归性。
答案 14 :(得分:0)
看看Outthentic,它很简单,可以通过多种语言(可选Perl,Python,Ruby,Bash)和跨平台(Linux,Windows)框架进行扩展,以测试任何命令行应用程序。
答案 15 :(得分:0)
你可能想看看bash_unit:
答案 16 :(得分:-3)
当 Python 具有如此巨大的优势时,我发现很难证明使用bash来处理更大的脚本:
if [ x"$foo" = x"$bar"]; then ...
”之类的模糊语法。getopt
模块轻松解析选项和参数(并且有一个更简单的模块来解析参数,但名称让我感到厌烦。)mysql
命令,但这不是编写代码的最好方法。)$*
或"$*"
或"$@"
或$1
或"$1"
,文件名中的空格不是问题等等。现在我只使用bash作为最简单的脚本。