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

pef*_*efu 6 bash shell unit-testing bats-core

我正在对一些遗留的 shell脚本进行单元测试.

在现实世界中的脚本通常被用来调用诸如实用程序find,tar,cpio,grep,sed,rsync,date等含有很多选择一些比较复杂的命令行.有时构造和使用正则表达式或通配符模式.

一个例子:这通常是由cron中定期调用的shell脚本必须从一台计算机的一些巨大的目录树镜像到另一个使用该实用程序的任务rsync的.应该从镜像过程中排除几种类型的文件和目录:

  #!/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
      ...
  }
  ...
Run Code Online (Sandbox Code Playgroud)

正如Michael Feathers在其着名的书"有效地使用遗留代码"中所写,一个好的单元测试运行得非常快,并且不会触及网络,文件系统或打开任何数据库.

根据Michael Feathers的建议,这里使用的技术是:依赖注入.这里要替换的对象是实用程序rsync.

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

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

另一个缺点是为复杂的实用程序编写一个类似的模型,rsync或者find容易出错并且是一个繁琐的工程任务.

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

小智 2

嘉吉的困境:

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

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

单元测试,顾名思义,将使您对正在设计的系统的统一部分充满信心。因此,对于 bash 脚本,您必须定义您的单位是什么。一个函数?脚本文件?一个命令?

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

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

并为其编写一个测试用例。并尽量不要偏离系统命令,因为它们是您所交付的系统的组成部分。