对shell脚本进行unit testing

几乎所有我多年来一直使用的产品都涉及到一定程度的shell脚本(或batch file,Windows上的PowerShell等)。 尽pipe我们用Java或C ++编写了大量的代码,但似乎总有一些集成或安装任务使用shell脚本可以更好地完成。

shell脚本因此成为交付代码的一部分,因此需要像编译代码那样进行testing。 有没有人有经验的一些shell脚本unit testing框架,如shunit2 ? 我现在主要对Linux shell脚本感兴趣, 我想知道testing工具如何复制其他xUnit框架的function和易用性,以及与CruiseControl或Hudson等连续构build系统集成是多么容易。

我在Linux环境中使用shunit2来处理与Java / Ruby Web应用程序相关的shell脚本。 这很容易使用,而不是从其他的xUnit框架大大偏离。

我还没有尝试与CruiseControl或Hudson / Jenkins集成,但通过其他方式实现持续集成,我遇到了这些问题:

  • 退出状态:当testing套件发生故障时,shunit2不会使用非零退出状态来通知故障。 所以你要么必须parsingshunit2输出来确定套件的通过/失败,要么改变shunit2的行为像一些持续集成框架所期望的那样,通过退出状态来沟通/不通过。
  • XML日志:shunit2不会生成结果的JUnit样式XML日志。

想知道为什么没人提到BATS 。 它是最新的,符合TAP的。

描述:

#!/usr/bin/env bats @test "addition using bc" { result="$(echo 2+2 | bc)" [ "$result" -eq 4 ] } 

跑:

 $ bats addition.bats ✓ addition using bc 1 tests, 0 failures 

@ blake-mizerany的综合听起来不错,今后我应该利用它,但这是我的“穷人”的方法来创buildunit testing:

  • 将所有可testing的function分开。
  • 将函数移动到一个外部文件中,比如说functions.sh并将其input到脚本中。 你可以使用source `dirname $0`/functions.sh来达到这个目的。
  • functions.sh的末尾,embedded你的testing用例,如果条件如下:

     if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then fi 
  • 您的testing是对函数的文字调用,然后是退出代码和variables值的简单检查。 我喜欢添加一个简单的实用程序function,如下所示,以便于编写:

     function assertEquals() { msg=$1; shift expected=$1; shift actual=$1; shift if [ "$expected" != "$actual" ]; then echo "$msg EXPECTED=$expected ACTUAL=$actual" exit 2 fi } 
  • 最后,直接运行functions.sh来执行testing。

下面是一个示例来展示该方法:

  #!/bin/bash function adder() { return $(($1+$2)) } ( [[ "${BASH_SOURCE[0]}" == "${0}" ]] || exit 0 function assertEquals() { msg=$1; shift expected=$1; shift actual=$1; shift /bin/echo -n "$msg: " if [ "$expected" != "$actual" ]; then echo "FAILED: EXPECTED=$expected ACTUAL=$actual" else echo PASSED fi } adder 2 3 assertEquals "adding two numbers" 5 $? ) 

综述: http : //bmizerany.github.com/roundup/

有一个链接到自述文件中的一篇文章详细解释它。

除了综合报告和shunit2,我对壳unit testing工具的概述还包括assert.sh和shelltestrunner 。

我大多同意综合作者对shunit2的批评(有些是主观的),所以我在查看文档和例子之后排除了shunit2。 虽然,它看起来很熟悉有一些与jUnit的经验。

在我看来,shelltestrunner是我所看过的最初始的工具,因为它使用简单的声明式语法来定义testing用例。 像往常一样,任何级别的抽象都会以一定的灵活性为代价提供一些便利。 尽pipe简单是有吸引力的,但是我发现这个工具对于我来说是太有限制了,主要是因为缺less定义setup / tearDown动作的方法(例如,在testing之前操作input文件,在testing之后删除状态文件等)。

我起初有点困惑,assert.sh只允许asserting输出或退出状态,而我需要两个。 足够长的时间来写几个testing用例使用综合。 但是我很快就发现了这个set -eset -e模式不方便,因为在某些情况下,预期非零退出状态是除stdout之外的一个通信结果,这使得testing用例在所述模式下失败。 其中一个样本显示解决scheme:

 status=$(set +e ; rup roundup-5 >/dev/null ; echo $?) 

但是如果我需要非零退出状态和输出呢? 当然,我可以在调用之前set +e ,并在整个testing用例中set -eset +e 。 但是这是违背综合的原则“一切都是一个断言” 。 所以感觉就像我开始反对这个工具。

到那时我已经意识到assert.sh的“缺点”只允许断言退出状态或输出实际上是一个非问题,因为我可以通过像这样的复合expression式的test

 output=$($tested_script_with_args) status=$? expected_output="the expectation" assert_raises "test \"$output\" = \"$expected_output\" -a $status -eq 2" 

由于我的需求是非常基本的(运行一套testing,显示一切正常或失败),我喜欢assert.sh的简单性,所以这就是我所select的。

在寻找一个简单的shelltesting框架,可以为Jenkins生成xml结果并且没有真正find任何东西的时候,我写了一个。

这是在sourceforge – 该项目的名字是jshu。

http://sourceforge.net/projects/jshu