对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 -e
的set -e
模式不方便,因为在某些情况下,预期非零退出状态是除stdout之外的一个通信结果,这使得testing用例在所述模式下失败。 其中一个样本显示解决scheme:
status=$(set +e ; rup roundup-5 >/dev/null ; echo $?)
但是如果我需要非零退出状态和输出呢? 当然,我可以在调用之前set +e
,并在整个testing用例中set -e
或set +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。