bats指哪几家公司
用Java,Ruby和Python等语言编写应用程序的软件开发人员拥有完善的库,可以帮助他们长期保持软件的完整性。 他们创建测试,以在结构化环境中通过一系列执行来运行应用程序,以确保其所有软件方面均按预期工作。
当这些测试在持续集成(CI)系统中自动化时,这些测试甚至会更加强大,在这种情况下,每次推送到源存储库都会导致测试运行,并且在测试失败时会立即通知开发人员。 这种快速反馈提高了开发人员对其应用程序功能完整性的信心。
Bash自动化测试系统( BATS )使开发人员能够编写Bash脚本和库,以将Java,Ruby,Python和其他开发人员所使用的相同做法应用于其Bash代码。
安装BATS
“ BATS GitHub”页面包含安装说明。 有两个BATS帮助程序库,它们提供更强大的断言或允许覆盖BATS使用的“测试任何协议”( TAP )输出格式。 可以将它们安装在标准位置,并由所有脚本提供。 对于要测试的每组脚本或库,在Git存储库中包含完整版本的BATS及其帮助程序库可能更方便。 这可以使用git子模块系统来完成。
以下命令会将BATS及其帮助程序库安装到Git存储库中的测试目录中。
git submodule init
git submodule add https:
// github.com
/ sstephenson
/ bats test
/ libs
/ bats
git submodule add https:
// github.com
/ ztombol
/ bats-assert test
/ libs
/ bats-assert
git submodule add https:
// github.com
/ ztombol
/ bats-support test
/ libs
/ bats-support
git add .
git commit
-m
'installed bats'
要克隆Git存储库并同时安装其子模块,请使用
--recurse-submodules标志到git clone 。
每个BATS测试脚本必须由bats可执行文件执行。 如果将BATS安装到源代码存储库的test / libs目录中,则可以使用以下命令调用测试:
. / test / libs / bats / bin / bats < path to test script >
或者,将以下内容添加到每个BATS测试脚本的开头:
#!/usr/bin/env ./test/libs/bats/bin/bats
load
'libs/bats-support/load'
load
'libs/bats-assert/load'
和chmod + x <测试脚本的路径> 。 这将a)使它们与./test/libs/bats中安装的BATS可执行,并且b)包括这些帮助程序库。 BATS测试脚本通常存储在测试目录中,并为要测试的脚本命名,但扩展名为.bats 。 例如,测试bin / build的BATS脚本应称为test / build.bats 。
您还可以通过将正则表达式传递给BATS来运行整套BATS测试文件,例如./test/lib/bats/bin/bats test / *。bats 。
组织库和脚本以覆盖BATS
Bash脚本和库必须以一种有效地将其内部工作暴露给BATS的方式进行组织。 通常,在调用或执行时运行许多命令的库函数和Shell脚本不适合进行有效的BATS测试。
例如, build.sh是许多人编写的典型脚本。 本质上是一大堆代码。 有些人甚至可能将这堆代码放入库中的函数中。 但是,不可能在BATS测试中运行大量代码并涵盖在单独的测试案例中可能遇到的所有类型的故障。 测试具有足够覆盖范围的这堆代码的唯一方法是将其分解为许多小的,可重用的,最重要的是可独立测试的功能。
向库添加更多功能很简单。 另外一个好处是,其中一些功能本身可以变得出奇的有用。 一旦你已经打破了你的库函数为大量的小功能,您可以在BATS测试源库和运行功能,你会任何其他命令对其进行测试。
Bash脚本还必须分解为多个功能,执行脚本时,脚本的主要部分应调用这些功能。 此外,还有一个非常有用的技巧,可以使使用BATS测试Bash脚本更加容易:将脚本主要部分中执行的所有代码都移到一个名为run_main之类的函数中 。 然后,将以下内容添加到脚本的末尾:
if
[
[
" ${BASH_SOURCE[0]} " ==
" ${0} "
]
]
then
run_main
fi
这段额外的代码做了一些特殊的事情。 它使脚本作为脚本执行时的行为与将其带到source中的环境不同。 通过使用此技巧,可以通过采购脚本库和测试各个功能,以与测试库相同的方式对脚本进行测试。 例如,将build.sh重构为具有更好的BATS可测试性 。
编写和运行测试
如上所述,BATS是一个TAP兼容测试框架,其语法和输出对于使用过其他TAP兼容测试套件(例如JUnit,RSpec或Jest)的用户来说是熟悉的。 它的测试被组织成单独的测试脚本。 测试脚本被组织为一个或多个描述性的@test块, 这些块描述了要测试的应用程序的单元。 每个@test块将运行一系列命令,这些命令准备测试环境,运行要测试的命令,并对断言和已测试命令的输出进行断言。 许多断言函数是随bats , bats-assert和bats-support库一起导入的,它们在BATS测试脚本的开头加载到环境中。 这是一个典型的BATS测试块:
@
test
"requires CI_COMMIT_REF_SLUG environment variable"
{
unset CI_COMMIT_REF_SLUG
assert_empty
" ${CI_COMMIT_REF_SLUG} "
run some_command
assert_failure
assert_output
--partial
"CI_COMMIT_REF_SLUG"
}
如果BATS脚本包含设置和/或拆卸功能,则BATS将在每个测试块运行之前和之后自动执行它们。 这样就可以创建环境变量,测试文件以及执行一个或所有测试所需的其他操作,然后在每次测试运行后将其删除。 Build.bats是对我们新格式化的build.sh脚本的完整BATS测试。 (此测试中的mock_docker命令将在以下关于模拟/存根的部分中进行解释。)
运行测试脚本时,BATS使用exec将每个@test块作为单独的子进程运行。 这样就可以在一个@test中导出环境变量甚至函数,而不会影响其他@test或污染您当前的shell会话。 测试运行的输出是一种标准格式,可以被人类理解,并且可以由TAP使用者以编程方式进行解析或操纵。 这是CI_COMMIT_REF_SLUG测试块失败时的输出示例:
✗ requires CI_COMMIT_REF_SLUG environment variable
( from
function
` assert_output
' in file test/libs/bats-assert/src/assert.bash, line 231,
in test file test/ci_deploy.bats, line 26)
`assert_output --partial "CI_COMMIT_REF_SLUG"' failed
-- output does not contain substring
--
substring
(
1 lines
) :
CI_COMMIT_REF_SLUG
output
(
3 lines
) :
.
/ bin
/ deploy.sh: join_string_by:
command not found
oc error
Could not
login
--
** Did not delete ,
as
test failed
**
1
test ,
1 failure
这是成功测试的输出:
✓ requires CI_COMMIT_REF_SLUG environment variable
帮手
像任何外壳程序脚本或库一样,BATS测试脚本可以包括帮助程序库,以在测试之间共享通用代码或增强其功能。 这些帮助程序库(例如bats-assert和bats-support )甚至可以使用BATS进行测试。
可以将库与BATS脚本放置在相同的测试目录中,或者如果测试目录中的文件数变得笨拙,则可以将它们放置在test / libs目录中。 BATS提供了加载功能,该功能采用相对于要测试的脚本(例如,在我们的例子中为test )相对于Bash文件的路径,并提供该文件的源代码。 文件必须以.bash前缀结尾 ,但是传递给load函数的文件路径不能包含前缀。 build.bats加载bats -assert和bats -support库,一个小helpers.bash库和docker_mock.bash库(如下所述),并在测试脚本的开始处将以下代码放置在解释器魔力线以下:
load
'libs/bats-support/load'
load
'libs/bats-assert/load'
load
'helpers'
load
'docker_mock'
存根测试输入并模拟外部调用
大多数Bash脚本和库在运行时都会执行功能和/或可执行文件。 通常,将它们编程为根据这些功能或可执行文件的退出状态或输出( stdout , stderr )以特定方式运行。 为了正确地测试这些脚本,通常需要制作这些命令的伪造版本,这些伪造版本旨在在特定测试(称为“存根”)的过程中以特定方式运行。 可能还需要监视正在测试的程序,以确保其调用了特定命令,或者使用特定参数调用了特定命令,此过程称为“模拟”。 有关更多信息,请查看适用于任何测试系统的有关Ruby RSpec中的模拟和存根的精彩讨论 。
Bash shell提供了一些技巧,可以在您的BATS测试脚本中使用这些技巧进行模拟和存根。 所有这些都要求使用带有-f标志的Bash 导出命令来导出覆盖原始函数或可执行文件的函数。 必须在执行测试的程序之前完成此操作。 这是一个覆盖cat可执行文件的简单示例:
function
cat
(
)
{
echo
"THIS WOULD CAT ${*} "
}
export
-f
cat
此方法以相同的方式覆盖函数。 如果测试需要覆盖要测试的脚本或库中的函数,则在对函数进行存根或模拟之前,必须先获取已测试的脚本或库的源代码,这一点很重要。 否则,在获取脚本时,存根/模拟将被实际功能替换。 另外,请确保在运行要测试的命令之前先进行存根/模拟。 这是来自build.bats的示例,该示例模拟 build.sh中描述的raise函数,以确保登录功能引发特定的错误消息:
@
test
".login raises on oc error"
{
source
${profile_script}
function raise
(
)
{
echo
" ${1} raised" ;
}
export
-f raise
run
login
assert_failure
assert_output
-p
"Could not login raised"
}
通常,没有必要在测试后取消存根/模拟功能,因为导出仅会在当前@test块的执行期间影响当前子进程 。 但是,可以模拟/存根BATS assert *函数在内部使用的命令(例如cat , sed等)。 在运行这些断言命令之前,必须取消设置这些模拟/存根函数,否则它们将无法正常工作。 这是来自build.bats的示例,该示例模拟 sed ,运行build_deployable函数并在运行任何断言之前取消设置sed :
@
test
".build_deployable prints information, runs docker build on a modified Dockerfile.production and publish_image when its not a dry_run"
{
local
expected_dockerfile =
'Dockerfile.production'
local
application =
'application'
local
environment =
'environment'
local
expected_original_base_image =
" ${application} "
local
expected_candidate_image =
" ${application} -candidate: ${environment} "
local
expected_deployable_image =
" ${application} : ${environment} "
source
${profile_script}
mock_docker build
--build-arg OAUTH_CLIENT_ID
--build-arg OAUTH_REDIRECT
--build-arg DDS_API_BASE_URL
-t
" ${expected_deployable_image} " -
function publish_image
(
)
{
echo
"publish_image ${*} " ;
}
export
-f publish_image
function
sed
(
)
{
echo
"sed ${*} "
>&
2 ;
echo
"FROM application-candidate:environment" ;
}
export
-f
sed
run build_deployable
" ${application} "
" ${environment} "
assert_success
unset
sed
assert_output
--regexp
"sed.* ${expected_dockerfile} "
assert_output
-p
"Building ${expected_original_base_image} deployable ${expected_deployable_image} FROM ${expected_candidate_image} "
assert_output
-p
"FROM ${expected_candidate_image} piped"
assert_output
-p
"build --build-arg OAUTH_CLIENT_ID --build-arg OAUTH_REDIRECT --build-arg DDS_API_BASE_URL -t ${expected_deployable_image} -"
assert_output
-p
"publish_image ${expected_deployable_image} "
}
有时,相同的命令(例如foo)将在被测试的同一函数中以不同的参数多次调用。 这些情况需要创建一组功能:
- mock_foo:将期望的参数作为输入,并将其持久化到TMP文件中
- foo:命令的模拟版本,该命令使用持久化的预期参数列表处理每个调用。 必须使用export -f将其导出。
- cleanup_foo:删除TMP文件,以用于拆卸功能。 这可以进行测试以确保在删除之前成功完成@test块。
由于此功能通常在不同的测试中重复使用,因此创建可以像其他库一样加载的帮助程序库是有意义的。
一个很好的例子是docker_mock.bash 。 它被加载到build.bats中,并在任何测试调用Docker可执行文件的功能的测试块中使用。 使用docker_mock的典型测试块如下所示:
@
test
".publish_image fails if docker push fails"
{
setup_publish
local
expected_image =
"image"
local
expected_publishable_image =
" ${CI_REGISTRY_IMAGE} / ${expected_image} "
source
${profile_script}
mock_docker tag
" ${expected_image} "
" ${expected_publishable_image} "
mock_docker push
" ${expected_publishable_image} " and_fail
run publish_image
" ${expected_image} "
assert_failure
assert_output
-p
"tagging ${expected_image} as ${expected_publishable_image} "
assert_output
-p
"tag ${expected_image} ${expected_publishable_image} "
assert_output
-p
"pushing image to gitlab registry"
assert_output
-p
"push ${expected_publishable_image} "
}
该测试建立了一个期望,即将使用不同的参数两次调用Docker。 在对Docker的第二次调用失败时,它将运行测试的命令,然后测试退出状态和对Docker的预期调用。
由mock_docker.bash引入的BATS的一个方面是$ {BATS_TMPDIR}环境变量,BATS在开始时对其进行了设置,以允许测试和帮助程序在标准位置创建和销毁TMP文件。 如果测试失败, mock_docker.bash库将不会删除其持久化的模拟文件,但会在其所在位置进行打印,以便可以查看和删除它。 您可能需要定期从该目录中清除旧的模拟文件。
关于嘲笑/存根的警告,请注意: build.bats测试有意识地违反了以下测试格言: 不要嘲笑您不拥有的东西! 该原则要求将对测试开发人员未编写的命令的调用(如docker , cat , sed等)包装在其自己的库中,并应在使用它们的脚本的测试中对其进行模拟。 然后,应该在不模拟外部命令的情况下测试包装器库。
这是一个很好的建议,而忽略它是有代价的。 如果Docker CLI API发生更改,则测试脚本将不会检测到此更改,从而导致误报,直到经过测试的build.sh脚本在具有新版本Docker的生产环境中运行时,该错误才会显现。 测试开发人员必须决定他们要严格遵守此标准的严格程度,但是他们应该了解决策所涉及的权衡。
结论
在任何软件开发项目中引入测试制度都会在a)开发和维护代码和测试所需的时间和组织增加与b)开发人员在其整个生命周期中对应用程序完整性的信心增加之间进行权衡。 测试方案可能不适用于所有脚本和库。
通常,应使用BATS测试满足以下一个或多个条件的脚本和库:
- 它们值得存储在源代码管理中
- 它们被用于关键过程中,并可以长期稳定运行
- 需要定期修改它们以添加/删除/修改其功能
- 他们被别人使用
一旦决定将测试规则应用于一个或多个Bash脚本或库,BATS将提供其他软件开发环境中可用的全面测试功能。
致谢:我感谢Darrin Mann向我介绍了BATS测试。
bats指哪几家公司