Bash测试中能否Mock内置command命令?环境依赖脚本如何测试?
问题:如何在Bats中Stub内置命令并测试依赖检查脚本?
我有一个脚本会在出错时检查所有依赖是否已安装,为此使用了内置的command命令。最简版本脚本script_to_test.sh如下:
command -v pdfgrep return $?
我使用bats-core编写了测试文件test.sh:
setup() { load 'test_helper/bats-support/load' load 'test_helper/bats-assert/load' load 'test_helper/mocks/stub' DIR="$( cd "$( dirname "$BATS_TEST_FILENAME" )" >/dev/null 2>&1 && pwd )" PATH="$DIR/..:$PATH" } @test "Test mocking built-ins" { stub command \ "-v pdfgrep : exit 17" run script_to_test.sh unstub command assert_failure 17 }
运行测试时出现报错:
`unstub command' failed
原因是被stub的command从未被调用。请问:是否可以Stub内置命令?如何测试依赖环境的脚本行为?
解答
能不能Stub内置命令?
不能直接用bats-mock的stub命令处理Shell内置命令。因为stub的原理是在PATH中添加优先级更高的伪造可执行文件,但Shell内置命令(比如command、echo)会直接被Shell执行,不会去PATH中查找外部命令,所以你的mock版本根本不会被触发,这就是报错的原因。
如何测试这类依赖检查脚本?
提供两种可行的解决方案:
方案1:修改被测脚本,引入可替换的检查命令变量
修改script_to_test.sh,把内置的command换成可通过环境变量覆盖的变量,不影响正常使用,仅在测试时替换:
# script_to_test.sh # 默认使用内置command,测试时可通过环境变量替换 CHECK_CMD="${CHECK_CMD:-command}" $CHECK_CMD -v pdfgrep return $?
对应的Bats测试代码:
@test "Test dependency not found" { # 用stub创建伪造的check_cmd命令 stub check_cmd \ "-v pdfgrep : exit 17" # 让脚本使用伪造的命令执行检查 CHECK_CMD="check_cmd" run script_to_test.sh unstub check_cmd assert_failure 17 }
方案2:在子Shell中用函数覆盖内置命令
不需要修改被测脚本,利用Shell函数可以覆盖内置命令的特性,在执行脚本的子Shell中定义mock函数:
@test "Test dependency not found with function mock" { # 在子Shell中定义mock的command函数,仅对当前测试生效 run bash -c ' command() { # 匹配目标调用参数时返回指定退出码 if [[ "$*" == "-v pdfgrep" ]]; then exit 17 fi # 其他情况调用真实的内置command builtin command "$@" } # 加载并执行被测脚本 source script_to_test.sh ' assert_failure 17 }
这种方式的优势是无需改动生产脚本,但要注意:必须在子Shell中定义mock函数,避免污染全局测试环境;如果脚本中command有其他用法,需要在mock函数中处理,或者用builtin command "$@"兜底调用真实命令。
内容的提问来源于stack exchange,提问作者Pascal
相关产品推荐
相关产品推荐

