阿里妹导读:Sharness 是一个用 Shell 脚本来编写测试用例的测试框架。本文将详细介绍 Sharness 的结构及测试用例的编写格式,以及语法规范和技巧,教大家如何使用 Sharness 编写测试用例,同时还分享了 Sharness 的扩展功能和项目实战。
文末福利:Hologres训练营,专家带练,玩转实时数仓。
Sharness 是一个用 Shell 脚本来编写测试用例的测试框架。
可以在 Linux、macOS 平台运行测试用例。
测试输出符合 TAP(test anything protocol),因此可以用 sharness 自身工具或 prove 等 TAP 兼容测试夹具(harness)运行。
是由Junio在2005年为Git项目开发的测试框架,由 Christian Couder (chriscool) 从 Git 中剥离为独立测试框架。
地址:https://github.com/chriscool/sharness
cat >expect <<-EOF
Hello, world.
EOF
test_expect_success “app output test” ‘
cat >expect <<-EOF &&
Hello, world.
EOF
hello-world >actual &&
test_cmp expect actual
‘
test_expect_success “name” ‘
<Script…>
‘
test_pause
test_done
待测应用放在项目的根目录。例如 Git 项目的待测应用: git 和 git-receive-pack 等。
测试框架修改 PATH 环境变量,使得测试用例在调用待测应用(如 git 命令)的时候,优先使用项目根目录下的待测应用。
测试脚本命名为 tNNNN-<name>.sh,即以字母 t 和四位数字开头的脚本文件。
每一个测试用例在执行时会创建一个独立的临时目录,例如 trash directory.t5323-pack-redundant。测试用例执行成功,则该目录会被删除。
#!/bin/sh
#
# Copyright (c) 2018 Jiang Xin
#
test_description='Test git pack-redundant
In order to test git-pack-redundant, we will create a number of objects and
packs in the repository `master.git`. The relationship between packs (P1-P8)
and objects (T, A-R) is showed in the following chart. Objects of a pack will
be marked with letter x, while objects of redundant packs will be marked with
exclamation point, and redundant pack itself will be marked with asterisk.
| T A B C D E F G H I J K L M N O P Q R
----+--------------------------------------
P1 | x x x x x x x x
P2* | ! ! ! ! ! ! !
P3 | x x x x x x
P4* | ! ! ! ! !
P5 | x x x x
P6* | ! ! !
P7 | x x
P8* | !
----+--------------------------------------
ALL | x x x x x x x x x x x x x x x x x x x
Another repository `shared.git` has unique objects (X-Z), while other objects
(marked with letter s) are shared through alt-odb (of `master.git`). The
relationship between packs and objects is as follows:
| T A B C D E F G H I J K L M N O P Q R X Y Z
----+----------------------------------------------
Px1 | s s s x x x
Px2 | s s s x x x
'
. ./test-lib.sh
master_repo=master.git
shared_repo=shared.git
# Create commits in <repo> and assign each commit's oid to shell variables
# given in the arguments (A, B, and C). E.g.:
#
# create_commits_in <repo> A B C
#
# NOTE: Avoid calling this function from a subshell since variable
# assignments will disappear when subshell exits.
create_commits_in () {
repo="$1" &&
if ! parent=$(git -C "$repo" rev-parse HEAD^{} 2>/dev/null)
then
... ...
test_expect_success 'setup master repo' '
git init --bare "$master_repo" &&
create_commits_in "$master_repo" A B C D E F G H I J K L M N O P Q R
'
#############################################################################
# Chart of packs and objects for this test case
#
# | T A B C D E F G H I J K L M N O P Q R
# ----+--------------------------------------
# P1 | x x x x x x x x
# P2 | x x x x x x x
# P3 | x x x x x x
# ----+--------------------------------------
# ALL | x x x x x x x x x x x x x x x
#
#############################################################################
test_expect_success 'master: no redundant for pack 1, 2, 3' '
create_pack_in "$master_repo" P1 <<-EOF &&
$T
$A
$B
$C
$D
$E
$F
$R
EOF
create_pack_in "$master_repo" P2 <<-EOF &&
$B
$C
$D
$E
$G
$H
$I
EOF
create_pack_in "$master_repo" P3 <<-EOF &&
$F
$I
$J
$K
$L
$M
EOF
(
cd "$master_repo" &&
git pack-redundant --all >out &&
test_must_be_empty out
)
'
test_done
待测应用放在项目的根目录。
测试脚本命名为 <name>.t,即扩展名为 .t 的脚本文件。
每一个测试用例在执行时会创建一个独立的临时目录,例如 trash directory.simple.t。测试用例执行成功,则该目录会被删除。
在 sharness.d 目录下添加自定义脚本,可以扩展 Sharness 框架。即在框架代码加载时,自动加载该目录下文件。
定制版本对测试框架代码做了进一步封装,框架代码放在单独的子目录。
测试脚本的名称恢复为和 Git 项目测试脚本类似的名称(tNNNN-<name>.sh), 即以字母 t 和四位数字开头的脚本文件。
#!/bin/sh
test_description="git-repo init"
. ./lib/sharness.sh
Create manifest repositories
manifest_url="file://${REPO_TEST_REPOSITORIES}/hello/manifests"
test_expect_success "setup" '
# create .repo file as a barrier, not find .repo deeper
touch .repo &&
mkdir work
'
test_expect_success "git-repo init -u" '
(
cd work &&
init -u $manifest_url
)
'
test_expect_success "manifest points to default.xml" '
(
cd work &&
test -f .repo/manifest.xml &&
echo manifests/default.xml >expect &&
readlink .repo/manifest.xml >actual &&
test_cmp expect actual
)
'
test_done
test_expect_success 'initial checksum' '
(
cd bare.git &&
git checksum --init &&
test -f info/checksum &&
test -f info/checksum.log
) &&
cat >expect <<-EOF &&
INFO[<time>]: initialize checksum
EOF
cat bare.git/info/checksum.log |
sed -e "s/\[.*\]/[<time>]/" >actual &&
test_cmp expect actual
'
test_expect_success !MINGW,!CYGWIN \
’correct handling of backslashes' '
rm -rf whitespace &&
mkdir whitespace &&
>"whitespace/trailing 1 " &&
>"whitespace/trailing 2 \\\\" &&
>"whitespace/trailing 3 \\\\" &&
>"whitespace/trailing 4 \\ " &&
>"whitespace/trailing 5 \\ \\ " &&
>"whitespace/trailing 6 \\a\\" &&
>whitespace/untracked &&
sed -e "s/Z$//" >ignore <<-\EOF &&
whitespace/trailing 1 \ Z
whitespace/trailing 2 \\\\Z
whitespace/trailing 3 \\\\ Z
whitespace/trailing 4 \\\ Z
whitespace/trailing 5 \\ \\\ Z
whitespace/trailing 6 \\a\\Z
EOF
echo whitespace/untracked >expect &&
git ls-files -o -X ignore whitespace >actual 2>err &&
test_cmp expect actual &&
test_must_be_empty err
'
test_expect_success 'shared: create new objects and packs' '
create_commits_in "$shared_repo" X Y Z &&
create_pack_in "$shared_repo" Px1 <<-EOF &&
$X
$Y
$Z
$A
$B
$C
EOF
create_pack_in "$shared_repo" Px2 <<-EOF
$X
$Y
$Z
$D
$E
$F
EOF
'
create_pack_in () {
repo="$1" &&
name="$2" &&
pack=$(git -C "$repo/objects/pack" pack-objects -q pack) &&
eval $name=$pack &&
eval P$pack=$name:$pack
}
test_expect_success 'master: one of pack-2/pack-3 is redundant' '
create_pack_in "$master_repo" P4 <<-EOF &&
$J
$K
$L
$M
$P
EOF
create_pack_in "$master_repo" P5 <<-EOF &&
$G
$H
$N
$O
EOF
(
cd "$master_repo" &&
cat >expect <<-EOF &&
P3:$P3
EOF
git pack-redundant --all >out &&
format_packfiles <out >actual &&
test_cmp expect actual
)
'
> +format_git_output () {
Unless this helper is able to take any git output and massage,
please describe what kind of git output it is meant to handle.
Also, "format" does not tell anything to the readers why it wants to
transform its input or what its output is supposed to look like. It
does not help readers and future developers.
+
# setup pre-receive hook +
+ cat >upstream/hooks/pre-receive <<-EOF &&
Wouldn't it make it easier to read the resulting text if you quoted
the end-of-here-text marker here, i.e. "<<\-EOF"? That way, you can
lose backslash before $old, $new and $ref.
> + #!/bin/sh
+
printf >&2 "# pre-receive hook\n" +
+
while read old new ref +
do +
printf >&2 "pre-receive< \$old \$new \$ref\n" +
done +
+ EOF
使用 tab 缩进。
规定 case 语句、if 语句的缩进格式。
输入输出重定向字符后面不要有空格。
使用 $(command) 而不是 `command` 。
使用 test 方法,不要使用 [ ... ] 。
test_expect_success
test_expect_failure
标记为存在已知问题,执行失败不报错,执行成功则警告该 broken 的用例已经 fixed。
test_must_fail
后面的一条命令必须失败。如果后面命令成功,测试失败。
test_expect_code
命令以给定返回值结束。
test_cmp
比较两个文件内容,相同成功,不同失败并显示差异。
test_path_is_file
参数必须是一个文件,且存在。
test_path_is_dir
参数必须是一个目录,且存在。
test_must_be_empty
参数指向的文件内容必须为空。
test_seq
跨平台的 seq,用户生成数字序列。
test_pause
测试暂停,进入子 Shell。
test_done
测试用例结束。
在 trash directory.* 目录下执行 git init 命令。目的是避免目录逃逸时误执行 git 命令影响项目本身代码。
例如:测试脚本在工作区下创建了一个仓库(git init my.repo),想要在该仓库下执行 git clean,却忘了进入到 my.repo 子目录再执行,结果导致待测试项目中丢失文件。
引入 Git 项目中的一些有用的测试方法。
如:test_tick 方法,可以设置 GIT_AUTHOR_DATE、GIT_COMMITTER_DATE 等环境变量,确保测试脚本多次运行时提交时间的一致性,进而产生一致的提交ID。
引入项目需要的其他自定义方法。
例如 git-repo 项目为了避免工作区逃逸,在 trash directory.* 目录下创建 .repo 文件。
相关链接
[1]https://www.worldhello.net/2013/10/26/test-gistore-using-git-test-framework.html [2]https://sourcegraph.com/github.com/git/git@master/-/tree/t [3]https://github.com/git/git/blob/master/t/t5323-pack-redundant.sh [4]https://github.com/alibaba/git-repo-go/blob/master/test/t0100-init.sh [5]https://github.com/git/git/blob/master/Documentation/CodingGuidelines [6]https://github.com/alibaba/git-repo-go [7]https://github.com/jiangxin/git/blob/jx/proc-receive-hook/t/helper/test-proc-receive.c [8]https://github.com/jiangxin/git/commit/9654f5eda1153634ab09ca5c6e490bcabdd57e61 [9]https://github.com/jiangxin/git/blob/jx/proc-receive-hook/t/t5411-proc-receive-hook.sh
实时数仓Hologres训练营
技术专家带练
5天打卡实时数仓Hologres训练营,赢限量T恤。5 位领域专家带你从HSAP与Hologres设计原理到上手实操,轻松玩转实时数仓。仅余600席!先抢先赢。
点击“阅读原文”,立即报名参加吧!