单元测试Bash脚本
我们有一个系统,除了Java代码外,还运行一些Bash脚本。由于我们正在尝试测试所有可能会中断的内容,而那些Bash脚本可能会中断,所以我们希望对它们进行测试 问题是很难测试Bash脚本 有没有一种方法或最佳实践来测试Bash脚本?或者我们应该停止使用Bash脚本并寻找可测试的替代解决方案吗?实际上有一个基于xUnit的单元测试框架,用于基于Bourne的shell脚本。我自己也没用过,但可能值得一看 以前也曾提出过类似的问题:单元测试Bash脚本,bash,testing,tdd,automated-tests,extreme-programming,bats-core,Bash,Testing,Tdd,Automated Tests,Extreme Programming,Bats Core,我们有一个系统,除了Java代码外,还运行一些Bash脚本。由于我们正在尝试测试所有可能会中断的内容,而那些Bash脚本可能会中断,所以我们希望对它们进行测试 问题是很难测试Bash脚本 有没有一种方法或最佳实践来测试Bash脚本?或者我们应该停止使用Bash脚本并寻找可测试的替代解决方案吗?实际上有一个基于xUnit的单元测试框架,用于基于Bourne的shell脚本。我自己也没用过,但可能值得一看 以前也曾提出过类似的问题: 为什么说测试Bash脚本“很难” 下面这样的测试包装器有什么
- 为什么说测试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
}
#/bin/bash
set-e
错误=0
结果=$($script\u在测试$args下我从一个讨论小组得到了以下答案:
可以导入(包括,
无论什么)程序(功能,
不管它叫什么)从外部
文件。这是编写
测试脚本:你打破了你的
将脚本转换为独立的过程
然后可以将其导入到这两个系统中
运行脚本和测试
脚本,然后你就可以开始跑步了
脚本应该尽可能简单
这种方法类似于脚本,听起来很合理。最好避免使用Bash脚本,使用更易测试且不太晦涩的语言。符合Bash测试要求:
TAP,测试任何协议,是一个简单的基于文本的测试模块之间的接口。TAP启动生命是Perl的测试线束的一部分,但是现在在C、C++、Python、PHP、Perl、java、JavaScript等中都有实现。
是一个Bash测试框架,我主要设计用于测试其他软件,但我也使用它来测试Bash模块,包括和
其主要优点是相对较低的编码开销、无限制的断言嵌套和灵活选择要验证的断言
我将它与Red Hat的一些人使用的框架进行了比较。我非常喜欢从Bash脚本测试中生成类似输出的实用程序。这非常有用,因为生成的报告可以由连续集成系统读取,例如用于和的JUnit插件
虽然shell2junit没有提供全面的Bash脚本框架,但它确实可以让您很好地报告测试结果。您可能想看看Bash\u单元:
试试。这是测试脚本的简单方法。例如,您需要做一些工作。sh
可以更改一些配置文件。例如,在配置文件/etc/my.cfg
中添加一行新的密码='XXXXX'
逐行编写Bash命令,然后检查输出
安装:
pip3 install bashtest
创建测试只是编写bash命令
文件test做一些工作。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
您可以找到和。也许这可以用于或有助于:
它的目的是在TAP协议中写入结果,我认为这对那些需要外壳环境的人是有益的。我认为有些东西在外壳环境中运行,所以有些人可能会认为应该在他们的外壳环境中测试它。试试看:
source.“/assert.sh”
本地预期实际
expected=“你好”
“现实世界!”
断言_eq“$expected”“$actual”“不等效!”
#=>x Hello==World::不等效!
Nikita Sobolev写了一篇优秀的博客文章,比较了几种不同的Bash测试框架:
对于不耐烦的人:Nikita的结论是使用,但似乎Nikita错过了这个项目,在我看来,这是一个继续使用的项目,因为最初的Bats项目自2013年以来一直没有得到积极维护。看一看。它很简单,可以通过多种语言(、、和Bash on choice)进行扩展,并且跨平台(Linux和Windows)框架来测试任何命令行应用程序。我真不敢相信没有人提到过!它与两者兼容,而且它是纯shell(也就是说,不涉及其他语言),也可以独立工作,而且简单直接
测试如下所示(取自项目页面的片段):
将其重命名为使用.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\u JUNIT
或通过-j
生成JUNIT输出。JUNIT还可以与prove(1)
组合使用
我使用了这个库来测试函数,方法是:将它们的文件来源化,然后使用IS
/OK
及其否定项运行断言,然后使用运行/NRUN
脚本。对我来说,这个框架以最少的开销提供了最大的收益。我已经尝试了这里介绍的很多解决方案,但是我发现它们中的大多数太庞大,难以使用,所以我构建了自己的小测试框架:
它只是存储库中的一个文件,您可以使用一组基本的样式断言直接运行它
我已经在几个内部项目中专业地使用了它,并且能够使我们的Bash脚本超级稳定和抗回归。我创建了它,因为我想要一个易于使用和有用的工具
它是由一个纯POSIX shell脚本编写的。它已经用比shunit2更多的shell进行了测试。它比bats/bats内核具有更强大的功能
例如,它支持嵌套块、易于模拟/存根、易于跳过/挂起、参数化测试、断言行号、按行号执行、并行执行、随机执行、/formatter、覆盖率和集成、探查器等
请参见项目页面上的演示
创建一个测试mytest.sh
。它使用特定的输入调用脚本
创建一个文本文件expected/mytest.stdout
。它包含
$ 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
$ 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
$ 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
mytest.sh > actual/mytest.stdout
diff expected/mytest.stdout actual/mytest.stdout