Shell脚本单元测试:如何模拟复杂的实用程序

时间:2016-03-25 11:39:16

标签: bash shell unit-testing bats-core

我进入了unit testinglegacy个shell脚本的rsync

在现实世界中,脚本通常用于调用实用程序 像findtarcpiogrepsedrsyncdate等等,有一些相当复杂的命令包含很多选项的行。有时构造并使用正则表达式或通配符模式。

示例:通常由cron以定期间隔调用的shell脚本的任务是使用实用程序Working Effectively with Legacy Code将一些巨大的目录树从一台计算机镜像到另一台计算机。 应该从中排除几种类型的文件和目录 镜像过程:

  #!/usr/bin/env bash
  ...
  function mirror() {
      ...
      COMMAND="rsync -aH$VERBOSE$DRY $PROGRESS $DELETE $OTHER_OPTIONS \
                   $EXCLUDE_OPTIONS $SOURCE_HOST:$DIRECTORY $TARGET"
      ...
      if eval $COMMAND
      then ...
      else ...
      fi
      ...
  }
  ...

正如Michael Feathers在其着名的书dependency injection中所写,一个好的单元测试运行速度非常快,不会触及网络,文件系统或打开任何数据库。

根据Michael Feathers的建议,这里使用的技术是:bats。这里要替换的对象是实用程序rsync

我的第一个想法:在我的shell脚本测试框架中(我使用mockup)我操纵$PATH的方式是找到mockup rsync而不是rsync 真正的rsync实用程序。此script under test对象可以检查提供的命令行参数和选项。与此mockup部分中使用的其他实用程序类似。

我过去在脚本编写方面存在实际问题的经验通常是由文件或目录名称中的特殊字符引起的错误,引用或编码问题,ssh密钥丢失,权限错误等等。这种类型的错误将逃脱这种单元测试技术。 (我知道:对于其中一些问题,单元测试根本不是治愈方法)。

另一个缺点是为find或{{1}}之类的复杂实用程序编写{{3}}容易出错,并且是一项繁琐的工程任务。

我认为上述情况足够普遍,以至于其他人可能遇到过类似的问题。谁有一些聪明的想法,并愿意与我分享这些想法?

2 个答案:

答案 0 :(得分:1)

嘉吉的窘境:

  

"任何设计问题都可以通过添加额外的间接级别来解决,除了太多的间接级别。"

为什么要模拟系统命令?毕竟,如果您正在编程Bash,系统是您的目标,您应该使用系统评估您的脚本。

单位测试,顾名思义,将使您对正在设计的系统的单一部分有信心。因此,在bash脚本的情况下,您必须定义 单元。一个功能?一个脚本文件?命令?

如果您想将单位定义为函数,我会建议您编写上面列出的众所周知的错误列表:

  • 文件或目录名称中的特殊字符
  • 引用或编码问题
  • 缺少ssh密钥
  • 错误的权限等。

并为它编写一个测试用例。并尝试不偏离系统命令,因为它们是您正在提供的系统的完整部分。

答案 1 :(得分:1)

您可以使用函数对任何命令进行模拟,如下所示:

function rsync() {
    # mock things here if necessary
}

然后导出该函数并运行unittest:

export -f rsync
unittest