几年来我所使用的每一件产品都涉及一定程度的shell脚本(或Windows上的批处理文件,PowerShell等).尽管我们用Java或C++编写了大量代码,但似乎总有一些集成或安装任务最好用shell脚本完成.
因此,shell脚本成为已发布代码的一部分,因此需要像编译代码一样进行测试.有没有人有过一些shell脚本单元测试框架的经验,比如shunit2?我现在主要对Linux shell脚本感兴趣; 我想知道测试工具如何复制其他xUnit框架的功能和易用性,以及与CruiseControl或Hudson等连续构建系统集成是多么容易.
更新2019-03-01:我的偏好现在是蝙蝠.我在小项目上使用了几年.我喜欢简洁明了的语法.我没有将它与CI/CD框架集成,但它的退出状态确实反映了套件的整体成功/失败,这比shunit2更好,如下所述.
以前的答案:
我正在将shunit2用于与Linux环境中的Java/Ruby Web应用程序相关的shell脚本.它易于使用,与其他xUnit框架没有很大的不同.
我没有尝试与CruiseControl或Hudson/Jenkins集成,但是通过其他方式实现持续集成我遇到了这些问题:
退出状态:当测试套件发生故障时,shunit2不会使用非零退出状态来通知故障.因此,您要么必须解析shunit2输出以确定套件的通过/失败,要么将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的综述听起来不错,我将来应该使用它,但这是我用于创建单元测试的"穷人"方法:
将所有可测试的东西分开作为一个功能.
将函数移动到外部文件中,比如说,functions.sh
并将source
其移入脚本中.你可以用它source `dirname $0`/functions.sh
来达到这个目的.
最后functions.sh
,将您的测试用例嵌入以下if条件:
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then fi
您的测试是对函数的文字调用,然后是对退出代码和变量值的简单检查.我想添加一个简单的实用函数,如下所示,以便于编写:
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
直接运行以执行测试.
以下是展示方法的示例:
#!/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 $? )
除了roundup和shunit2之外,我对shell单元测试工具的概述还包括assert.sh和shelltestrunner.
我大多同意综合作者对shunit2的批评(其中一些是主观的),所以在查看文档和示例后我排除了shunit2.虽然看起来很熟悉有一些jUnit的经验.
在我看来,shelltestrunner是我看过的最原始的工具,因为它使用简单的声明性语法来进行测试用例定义.像往常一样,任何抽象级别都会以一些灵活性为代价提供一些便利.尽管简单性很有吸引力,但我发现该工具对我的情况也有限制,主要是因为缺乏定义setup/tearDown操作的方法(例如,在测试之前操作输入文件,在测试后删除状态文件)等).
我起初有点困惑,assert.sh只允许断言输出或退出状态,而我需要两者.足够长,可以使用综合编写几个测试用例.但我很快发现综合set -e
模式不方便,因为在某些情况下,预期非零退出状态作为除了stdout之外传递结果的一种方式,这使得测试用例在所述模式下失败.其中一个样本显示了解决方案:
status=$(set +e ; rup roundup-5 >/dev/null ; echo $?)
但是如果我需要非零退出状态和输出呢?我可以,当然,set +e
调用之前和set -e
之后或set +e
整个测试案例.但这违反了综述的原则"一切都是断言".所以我觉得我开始反对这个工具了.
到那时我已经意识到assert.sh允许仅断言退出状态或输出的"缺点"实际上是一个非问题,因为我可以传递test
一个像这样的复合表达式
output=$($tested_script_with_args) status=$? expected_output="the expectation" assert_raises "test \"$output\" = \"$expected_output\" -a $status -eq 2"
由于我的需求非常基本(运行一套测试,显示一切正常或失败),我喜欢assert.sh的简单性,所以这就是我选择的.