使用 Bats 框架测试bash脚本
1. 简介
Bash 是一种非常有用且流行的脚本语言,具有大量可能的用例。尽管该语言本身非常普遍,但测试它的想法并不像 Java 等语言那样普遍。这可能会导致代价高昂的错误并降低对代码的信心。
在本教程中,我们将学习如何改变这种状况并使用 Bats 框架来测试我们的 Bash 脚本。
2. 设置
2.1. 安装Bats
首先,我们需要安装Bats 。可以通过几种方式 完成,但我们将选择最直接的一种。我们只需将所需的存储库从 GitHub 作为子模块克隆到我们的项目中:
$ git init
$ git submodule add https://github.com/bats-core/bats-core.git test/bats
$ git submodule add https://github.com/bats-core/bats-support.git test/test_helper/bats-support
$ git submodule add https://github.com/bats-core/bats-assert.git test/test_helper/bats-assert
$ git submodule add https://github.com/bats-core/bats-assert.git test/test_helper/bats-files
2.2. 创建要测试的项目
其次,我们需要一些东西来测试。因此,让我们创建一个简单的脚本来连接字符串:
#!/usr/bin/env bash
concatenated="$1$2"
echo "$concatenated"
echo "$concatenated" > $3
exit 0
它会向控制台打印一个新字符串,但也会将其保存到一个文件中。该文件将根据脚本的第三个参数命名。我们将这个小程序命名为project.sh并将其放在src目录中。
2.3. 配置测试服
最后,我们需要创建和设置测试文件。让我们创建test目录并在其中放置一个空的test.bats文件。在编写任何实际测试之前,我们需要加载我们想要使用的所有 Bats 助手。此外,我们会将src目录添加到路径变量中以简化编写测试:
#!/usr/bin/env bash
setup() {
load 'test_helper/bats-support/load'
load 'test_helper/bats-assert/load'
load 'test_helper/bats-file/load'
DIR="$( cd "$( dirname "$BATS_TEST_FILENAME" )" >/dev/null 2>&1 && pwd )"
PATH="$DIR/../src:$PATH"
}
3. 简单测试
**我们可以编写的最基本的测试将检查我们的脚本是否会无错运行。**目前我们忽略脚本的输出,我们只想知道它是否完成:
@test "should run script" {
project.sh 'Hello ' 'Blogdemo' '/tmp/output'
}
要运行测试,我们需要从克隆的存储库调用 Bats 库:
$ ./test/bats/bin/bats test/test.bats
✓ should run script
1 tests, 0 failures, 0 skipped
4. 断言脚本的输出
因为我们 在设置方法中加载了bats-assert组件,所以我们可以使用提供的帮助程序快速在输出字符串上编写断言。要检查输出是否与提供的字符串完全匹配,我们需要使用assert_output助手:
@test "should return concatenated strings" {
run project.sh 'Hello ' 'Blogdemo' '/tmp/output'
assert_output 'Hello Blogdemo'
}
在许多情况下,输出又长又复杂,因此一对一比较是不切实际的。幸运的是,我们可以检查是否只有部分字符串匹配:
assert_output --partial 'Blogdemo'
最后,我们还可以检查输出是否与提供的字符串不匹配:
refute_output 'Hello World'
5. 断言文件
5.1. 断言存在
我们的脚本将连接的字符串保存到文件中,我们想测试该功能。值得庆幸的是,bats-file 组件为我们提供了方便的助手来断言文件的存在。让我们编写一个测试来检查输出文件是否存在:
@test "should create file" {
run project.sh 'Hello ' 'Blogdemo' '/tmp/output'
assert_exist /tmp/output
}
5.2. 打扫干净
每次我们执行脚本时,它都会创建一个输出文件。这会产生各种问题,从在磁盘上造成混乱到使测试不确定。幸运的是,有一个简单的解决方案:我们应该在每次测试后删除所有临时文件。但是,我们不需要在每个测试中都自己实现它。相反,我们可以使用一个特殊的拆解 函数:
teardown() {
rm -f /tmp/output
}
它会在每次测试后由 Bats 自动执行。如果我们想在所有测试后只执行一次,我们也可以使用teardown_file 函数。
6. 其他断言
断言助手非常方便,但功能有限。**如果我们需要超越它们,我们可以断言任何用 Bash 编写的布尔表达式。**例如,我们已经断言输出文件存在,但我们没有检查它是否包含正确的内容。让我们通过使用cat命令将它加载到变量 并使用条件表达式检查它来做到这一点:
@test "should write to file" {
run project.sh 'Hello ' 'Blogdemo' '/tmp/output'
file_content=`cat /tmp/output`
[ "$file_content" == 'Hello Blogdemo' ]
}
7. 跳过测试
有时我们需要排除测试。也许我们为尚未实现的功能编写了测试,或者有一个我们暂时想忽略的错误。我们可以将其注释掉,但这会产生简单地忘记它的可能性并且不是很优雅。相反,我们将使用skip 助手来标记要忽略的测试:
@test "should write logs" {
skip "Logs are not implemented yet"
run project.sh 'Hello ' 'Blogdemo' '/tmp/output'
file_content=`cat /tmp/logs`
[ "$file_content" == 'I logged something' ]
}
现在,如果我们运行测试,我们将获得有关跳过的测试的信息:
$ ./test/bats/bin/bats test/test.bats
✓ should run script
✓ should return concatenated strings
✓ should create file
✓ should write to file
- should write logs (skipped: Logs are not implemented yet)
5 tests, 0 failures, 1 skipped
8. 测试任何东西
因为 Bash 经常用于执行其他程序并对它们产生的结果进行操作,所以Bats 可以有效地用于测试任何东西。假设我们的项目需要在版本 12 中安装节点。我们可以通过使用*–version标志执行node*然后断言输出来检查它:
@test "test node version" {
run node --version
assert_output --partial "v12"
}
如果返回的字符串不包含“v12”或者根本没有安装node,测试将失败。