shell脚本的单元测试

时间:2009-06-09 19:11:46

标签: linux unit-testing shell scripting

多年来我所使用的每一件产品都涉及到某种程度的shell脚本(或Windows上的批处理文件,PowerShell等)。尽管我们用Java或C ++编写了大量代码,但似乎总有一些集成或安装任务最好用shell脚本完成。

shell脚本因此成为已发布代码的一部分,因此需要像编译代码一样进行测试。有没有人有使用某些shell脚本单元测试框架的经验,例如shunit2?我现在主要对Linux shell脚本感兴趣;我想知道测试工具如何复制其他xUnit框架的功能和易用性,以及与CruiseControl或Hudson等连续构建系统集成是多么容易。

8 个答案:

答案 0 :(得分:47)

更新2019-03-01:我现在的偏好是bats。我在小项目上使用了几年。我喜欢简洁明了的语法。我没有将它与CI / CD框架集成,但它的退出状态确实反映了套件的整体成功/失败,这比shunit2更好,如下所述。


以前的答案:

我正在将shunit2用于与Linux环境中的Java / Ruby Web应用程序相关的shell脚本。它易于使用,与其他xUnit框架没有很大的不同。

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

  • 退出状态:当测试套件发生故障时,shunit2不会使用非零退出状态来通知故障。因此,您必须解析shunit2输出以确定套件的通过/失败,或者将shunit2更改为一些持续集成框架所期望的行为,通过退出状态传递通过/失败。
  • XML日志:shunit2不会生成结果的JUnit样式XML日志。

答案 1 :(得分:26)

想知道为什么没有人提到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

答案 2 :(得分:20)

@ blake-mizerany的综述听起来很棒,我将来应该使用它,但这是我用于创建单元测试的“穷人”方法:

  • 将所有可测试的功能分开。
  • 将函数移动到外部文件中,将functions.shsource放入脚本中。您可以使用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 $?
    )

答案 3 :(得分:17)

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

README中有一篇文章的链接详细解释了它。

答案 4 :(得分:8)

roundupshunit2外,我对shell单元测试工具的概述还包括assert.shshelltestrunner

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

在我看来,shelltestrunner是我看过的最原始的工具,因为它使用简单的声明性语法进行测试用例定义。像往常一样,任何抽象级别都会以一些灵活性为代价提供一些便利。尽管简单性很有吸引力,但我发现该工具对我的情况也有限制,主要是因为缺乏定义setup / tearDown操作的方法(例如,在测试之前操作输入文件,在测试后删除状态文件)等等。)。

我起初有点困惑,assert.sh只允许断言输出或退出状态,而我需要两者。足够长,可以使用综合编写几个测试用例。但是我很快发现综合的set -e模式不方便,因为在某些情况下,除了stdout之外,预期非零退出状态作为传达结果的一种方式,这使得测试用例在所述模式中失败。 One of the samples显示了解决方案:

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

但是如果我需要非零退出状态和输出呢?当然,我可以在调用之前set +e,在整个测试用例之后set -e之后或set +e。但这违反了综述的原则"Everything is an Assertion"。所以我觉得我开始反对这个工具了。

到那时我已经意识到assert.sh允许仅断言退出状态或输出的“缺点”实际上是一个非问题,因为我可以使用像这样的复合表达式传入test / p>

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

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

答案 5 :(得分:2)

在寻找一个简单的shell单元测试框架后,可以为Jenkins生成xml结果而不是真正找到任何东西,我写了一个。

它在sourceforge上 - 项目的名称是jshu。

http://sourceforge.net/projects/jshu

答案 6 :(得分:2)

你应该尝试assert.sh lib,非常方便,易于使用

local expected actual
expected="Hello"
actual="World!"
assert_eq "$expected" "$actual" "not equivalent!"
# => x Hello == World :: not equivalent! 

答案 7 :(得分:1)

我最近发布了一个名为shellspec的新测试框架。

shellspec是BDD样式测试框架。 它适用于POSIX兼容的shell脚本,包括bash,破折号,ksh,busybox等。

当然,退出状态反映了规范运行的结果 并且具有符合TAP的格式化程序。

specfile接近自然语言,易于阅读, 也是与Shell脚本兼容的语法。

#shellcheck shell=sh

Describe 'sample'
  Describe 'calc()'
    calc() { echo "$(($*))"; }

    It 'calculates the formula'
      When call calc 1 + 2
      The output should equal 3
    End
  End
End