单元测试bash脚本

nim*_*cap 101 testing bash tdd automated-tests extreme-programming

我们有一个除了Java代码之外还运行一些bash脚本的系统.由于我们正在尝试测试可能会破坏的所有内容,并且这些bash脚本可能会中断,我们希望对它们进行测试.

问题是很难测试bash脚本.

有没有办法或最佳实践来测试bash脚本?或者我们应该退出使用bash脚本并寻找可测试的替代解决方案?

ire*_*ses 45

实际上有一个shell脚本单元测试框架.我自己没有用过,但可能值得一试.

之前曾提出过类似的问题:

  • 我可以断言(双关语)shunit2(版本2.1.6)到目前为止有点破碎.即使您为它们提供直接值,assertNull和assertNotNull也不起作用.assertEquals工作正常,但我想我现在只需要自己动手了. (2认同)
  • 我是shunit2的用户,有时甚至是其贡献者,我可以确认该项目在2019年仍然有效。 (2认同)

nim*_*cap 29

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

它可以从外部文件导入(包括,无论如何)一个过程(函数,无论它的命名).这是编写测试脚本的关键:您将脚本分解为独立的过程,然后可以将其导入到运行脚本和测试脚本中,然后让您的运行脚本尽可能简单.

这种方法就像脚本的依赖注入一样,听起来很合理.避免使用bash脚本并使用更易测试且不那么模糊的语言是可取的.

  • 虽然bash没有任何问题(我编写了很多很多脚本),但这是一种难以掌握的语言.我的经验法则是,如果脚本足够大以至于需要测试,那么您应该继续使用易于测试的脚本语言. (9认同)
  • 我不确定我是应该投票还是投票,一方面分成较小的部分是好的,但另一方面我需要一个框架而不是一组自定义脚本 (4认同)
  • 在测试方面,imho,模块化是好的. (4认同)
  • 我将遵循有关不使用 bash 的建议。:) (2认同)

Jan*_*sen 25

符合TAP标准的Bash测试:Bash自动测试系统

TAP,Test Anything Protocol,是测试工具中测试模块之间的简单基于文本的界面.TAP最初是Perl测试工具的一部分,但现在有C,C++,Python,PHP,Perl,Java,JavaScript等实现.

  • 值得披露的是TAP是什么以及为什么要关注,否则它只是无意义的复制粘贴 (14认同)
  • 因为没有其他人说这是不可能的:TAP =测试任何协议 (6认同)

spb*_*ick 7

Epoxy是一个Bash测试框架,我主要是为测试其他软件而设计的,但我也用它来测试bash模块,包括它自己Carton.

主要优点是相对较低的编码开销,无限制的断言嵌套和灵活的断言选择以进行验证.

我做了一个演示,将它与BeakerLib进行了比较- 这是Red Hat的一些人使用的框架.


cb2*_*cb2 7

Nikita Sobolev写了一篇很棒的博客文章,比较了几种不同的bash测试框架:测试Bash应用程序

对于不耐烦的人:Nikita的结论是使用Bats,但是Nikita似乎错过了Bats-core项目,在我看来,这是一个可以继续使用的项目,因为自2013年以来一直没有积极维护原始的Bats项目。


Jim*_*nis 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
 }
Run Code Online (Sandbox Code Playgroud)

  • 更重要的是,测试shell脚本很困难,因为它们通常具有大量副作用并利用系统资源,如文件系统,网络等.理想情况下,单元测试是无副作用的,不依赖于系统资源. (9认同)
  • 首先,bash脚本不是很易读.其次,期望是复杂的,比如检查是否使用创建它的bash脚本的PID创建了锁文件. (4认同)

Koi*_*ima 6

我创建了shellspec,因为我想要一个易于使用且有用的工具。

它由纯 POSIX shell 脚本编写。它已经通过比shunit2更多的shell进行了测试。它具有比 bats/bats-core 更强大的功能。

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

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


Dan*_*ral 5

我不敢相信没有人谈论OSHT!这是兼容两种 TAPJUnit的,这是纯粹的外壳(即,没有其他语言参与),它的工作原理独立过,而且它的简单和直接。

测试看起来像这样(摘自项目页面的片段):

#!/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
Run Code Online (Sandbox Code Playgroud)

一个简单的运行:

$ 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
Run Code Online (Sandbox Code Playgroud)

最后一个测试显示为“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
Run Code Online (Sandbox Code Playgroud)

重命名它以使用.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
Run Code Online (Sandbox Code Playgroud)

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

我使用了这个库,通过获取它们的文件然后使用IS/OK和它们的否定运行断言来测试功能,并使用RUN/运行脚本NRUN。对我来说,这个框架以最少的开销提供了最大的收益。