单元测试Bash脚本

单元测试Bash脚本,bash,testing,tdd,automated-tests,extreme-programming,bats-core,Bash,Testing,Tdd,Automated Tests,Extreme Programming,Bats Core,我们有一个系统,除了Java代码外,还运行一些Bash脚本。由于我们正在尝试测试所有可能会中断的内容,而那些Bash脚本可能会中断,所以我们希望对它们进行测试 问题是很难测试Bash脚本 有没有一种方法或最佳实践来测试Bash脚本?或者我们应该停止使用Bash脚本并寻找可测试的替代解决方案吗?实际上有一个基于xUnit的单元测试框架,用于基于Bourne的shell脚本。我自己也没用过,但可能值得一看 以前也曾提出过类似的问题: 为什么说测试Bash脚本“很难” 下面这样的测试包装器有什么

我们有一个系统,除了Java代码外,还运行一些Bash脚本。由于我们正在尝试测试所有可能会中断的内容,而那些Bash脚本可能会中断,所以我们希望对它们进行测试

问题是很难测试Bash脚本

有没有一种方法或最佳实践来测试Bash脚本?或者我们应该停止使用Bash脚本并寻找可测试的替代解决方案吗?

实际上有一个基于xUnit的单元测试框架,用于基于Bourne的shell脚本。我自己也没用过,但可能值得一看

以前也曾提出过类似的问题:

    • 为什么说测试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
       }
      
      #/bin/bash
      set-e
      错误=0
      
      结果=$($script\u在测试$args下我从一个讨论小组得到了以下答案:

      可以导入(包括, 无论什么)程序(功能, 不管它叫什么)从外部 文件。这是编写 测试脚本:你打破了你的 将脚本转换为独立的过程 然后可以将其导入到这两个系统中 运行脚本和测试 脚本,然后你就可以开始跑步了 脚本应该尽可能简单

      这种方法类似于脚本,听起来很合理。最好避免使用Bash脚本,使用更易测试且不太晦涩的语言。

      符合Bash测试要求:

      TAP,测试任何协议,是一个简单的基于文本的测试模块之间的接口。TAP启动生命是Perl的测试线束的一部分,但是现在在C、C++、Python、PHP、Perl、java、JavaScript等中都有实现。 是一个Bash测试框架,我主要设计用于测试其他软件,但我也使用它来测试Bash模块,包括和

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

      我将它与Red Hat的一些人使用的框架进行了比较。

      我非常喜欢从Bash脚本测试中生成类似输出的实用程序。这非常有用,因为生成的报告可以由连续集成系统读取,例如用于和的JUnit插件


      虽然shell2junit没有提供全面的Bash脚本框架,但它确实可以让您很好地报告测试结果。

      您可能想看看Bash\u单元:

      试试。这是测试脚本的简单方法。例如,您需要
      做一些工作。sh
      可以更改一些配置文件。例如,在配置文件
      /etc/my.cfg
      中添加一行新的
      密码='XXXXX'

      逐行编写Bash命令,然后检查输出

      安装:

      pip3 install bashtest
      
      创建测试只是编写bash命令

      文件
      test做一些工作。bashtest

      # Run the script
      $ ./do-some-work.sh > /dev/null
      
      # Testing that the line "PASSWORD = 'XXXXX'" is in the file /etc/my.cfg
      $ grep -Fxq "PASSWORD = 'XXXXX'" /etc/my.cfg && echo "YES"
      YES
      
      运行测试:

      bashtest *.bashtest
      

      您可以找到和。

      也许这可以用于或有助于:

      它的目的是在TAP协议中写入结果,我认为这对那些需要外壳环境的人是有益的。我认为有些东西在外壳环境中运行,所以有些人可能会认为应该在他们的外壳环境中测试它。

      试试看:

      source.“/assert.sh”
      本地预期实际
      expected=“你好”
      “现实世界!”
      断言_eq“$expected”“$actual”“不等效!”
      #=>x Hello==World::不等效!
      
      Nikita Sobolev写了一篇优秀的博客文章,比较了几种不同的Bash测试框架:


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

      看一看。它很简单,可以通过多种语言(、、和Bash on choice)进行扩展,并且跨平台(Linux和Windows)框架来测试任何命令行应用程序。

      我真不敢相信没有人提到过!它与两者兼容,而且它是纯shell(也就是说,不涉及其他语言),也可以独立工作,而且简单直接

      测试如下所示(取自项目页面的片段):

      将其重命名为使用
      .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
      
      设置
      OSHT\u JUNIT
      或通过
      -j
      生成JUNIT输出。JUNIT还可以与
      prove(1)
      组合使用


      我使用了这个库来测试函数,方法是:将它们的文件来源化,然后使用
      IS
      /
      OK
      及其否定项运行断言,然后使用
      运行
      /
      NRUN
      脚本。对我来说,这个框架以最少的开销提供了最大的收益。

      我已经尝试了这里介绍的很多解决方案,但是我发现它们中的大多数太庞大,难以使用,所以我构建了自己的小测试框架:

      它只是存储库中的一个文件,您可以使用一组基本的样式断言直接运行它

      我已经在几个内部项目中专业地使用了它,并且能够使我们的Bash脚本超级稳定和抗回归。

      我创建了它,因为我想要一个易于使用和有用的工具

      它是由一个纯POSIX shell脚本编写的。它已经用比shunit2更多的shell进行了测试。它比bats/bats内核具有更强大的功能

      例如,它支持嵌套块、易于模拟/存根、易于跳过/挂起、参数化测试、断言行号、按行号执行、并行执行、随机执行、/formatter、覆盖率和集成、探查器等

      请参见项目页面上的演示


      创建一个测试
      mytest.sh
      。它使用特定的输入调用脚本

      创建一个文本文件
      expected/mytest.stdout
      。它包含
      $ 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
      
      $ 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
      
      $ 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
      
      mytest.sh > actual/mytest.stdout
      
      diff expected/mytest.stdout actual/mytest.stdout