Gore Liu 6 years ago
parent
commit
fde1601a50

+ 1 - 0
00_Zsh-开发指南(目录).md

@@ -302,6 +302,7 @@
 ### [导读](21_Zsh-开发指南(第二十一篇-测试方法以及编写可测试代码的方法).md#导读)
 ### [单元测试](21_Zsh-开发指南(第二十一篇-测试方法以及编写可测试代码的方法).md#单元测试)
 ### [单个脚本的功能测试](21_Zsh-开发指南(第二十一篇-测试方法以及编写可测试代码的方法).md#单个脚本的功能测试)
+### [功能测试示例](21_Zsh-开发指南(第二十一篇-测试方法以及编写可测试代码的方法).md#功能测试示例)
 ### [集成测试](21_Zsh-开发指南(第二十一篇-测试方法以及编写可测试代码的方法).md#集成测试)
 ### [系统测试](21_Zsh-开发指南(第二十一篇-测试方法以及编写可测试代码的方法).md#系统测试)
 ### [总结](21_Zsh-开发指南(第二十一篇-测试方法以及编写可测试代码的方法).md#总结)

+ 174 - 1
21_Zsh-开发指南(第二十一篇-测试方法以及编写可测试代码的方法).md

@@ -20,6 +20,179 @@ Shell 脚本的最小测试粒度是单个脚本。必须保证单个脚本是
 
 对于有复杂外部依赖的脚本,可以写脚本自动测试,也可以手动测试,测试时需要包含正常和异常的情况,不能只测试正常情况。
 
+### 功能测试示例
+
+**需要写脚本完成如下功能:**
+
+如果 process1 和 process2 两个进程都存在,以 process2 进程 cwd 目录中的 `data/output.txt` 为输入,做一些比较复杂的处理,然后输出到 process1 进程 cwd 目录中的 `data/input.txt` 文件(如果该文件已存在,则不处理),处理完后,删除之前的 `data/output.txt`。
+
+**分析:**
+
+process1 和 process2 两个进程都是复杂的外部依赖,不能在主体逻辑脚本里直接依赖它们,所以要把检查进程是否存在的逻辑独立成单独的脚本。输入和输出文件的路径依赖进程路径,为了测试方便,也要把获取文件路径的逻辑独立成单独的脚本。
+
+**脚本功能实现:**
+
+检查进程是否存在和获取进程 cwd 目录的 util.zsh 脚本:
+
+```
+#!/bin/zsh
+
+check_process() {
+    pidof $1
+}
+
+get_process_cwd() {
+    readlink /proc/$1/cwd
+}
+```
+
+
+主体逻辑脚本 main.zsh:
+
+```
+#!/bin/zsh
+
+# 有错误即退出,可以省掉很多错误处理的代码
+set -e
+
+# 切换到脚本当前目录
+cd ${0:h}
+
+# 加载依赖的脚本
+source ./util.zsh
+
+# 检查进程是否存在
+local process1_pid=$(check_process process1)
+local process2_pid=$(check_process process2)
+
+# 这里的 input 和 output 是相对脚本来说的
+local input_file=$(get_process_cwd $process2_pid)/data/output.txt
+local output_file=$(get_process_cwd $process1_pid)/data/input.txt
+
+# 如果输入文件不存在,直接退出
+[[ -e $input_file ]] || {
+    echo $input_file not found.
+    exit 1
+}
+
+# 如果输出文件已存在,也直接退出
+[[ -e $output_file ]] && {
+    echo $output_file already exists.
+    exit 0
+}
+
+# 处理 $input_file 内容
+# 省略
+
+# 将结果输出到 $output_file
+# 省略
+```
+
+**功能测试方法:**
+
+util.zsh 里的两个函数功能过于简单,无需测试。
+
+测试 main.zsh 时,需要构造一系列测试用的 util.zsh,用于模拟各种情况:
+
+```
+# 进程存在的情况
+check_process() {
+    echo $$
+}
+
+# 进程不存在的情况
+check_process() {
+    return 1
+}
+
+# 进程 process1 存在而 process2 不存在的情况
+check_process() {
+    [[ $1 == process1 ]] && echo 1234 && return
+    [[ $1 == process2 ]] && return 1
+}
+
+# 输出了进程号,但实际进程不存在的情况
+check_process() {
+    echo 0
+}
+
+# 路径存在的情况
+get_process_cwd() {
+    [[ $1 == process1 ]] && echo /path/to/cwd1 && return
+    [[ $1 == process2 ]] && echo /path/to/cwd2 && return
+}
+
+# 路径不存在的情况
+get_process_cwd() {
+    return 1
+}
+
+# 输出了路径,但路径实际不存在的情况
+get_process_cwd() {
+    echo /wrong/path
+}
+```
+
+然后组合这些情况,写测试脚本判断 main.zsh 的处理是否符合预期。
+
+其中一个测试脚本样例:
+
+util_test1.zsh 内容:
+
+```
+#!/bin/zsh
+
+# 进程存在
+check_process() {
+    echo $$
+}
+
+# 直接返回正确的路径
+get_process_cwd() {
+    [[ $1 == process1 ]] && echo /path/to/cwd1 && return
+    [[ $1 == process2 ]] && echo /path/to/cwd2 && return
+}
+```
+
+test.zsh 内容:
+
+```
+#!/bin/zsh
+
+# 用于测试的函数,可以独立成单独脚本以便复用
+assert_ok() {
+    (($1 == 0)) || {
+        echo Error, retcode: $1
+        exit 1
+    }
+}
+
+check_output_file() {
+    # 检查输出文件是否符合预期
+    # 省略
+}
+
+# 应用 util_test1.zsh
+ln -sf util_test1.zsh util.zsh
+
+# 运行脚本
+./main.zsh
+
+# 检查返回值是否正常
+assert_ok $?
+
+# 检查输出文件是否符合预期
+check_output_file /path/to/output/file
+
+# 其他检查
+# 省略
+
+# 应用 util_test2.zsh
+ln -sf util_test2.zsh util.zsh
+
+# 省略
+```
+
 ### 集成测试
 
 测试完每个脚本的功能后,需要将各个脚本以及其他程序整合起来测试互相调用过程是否正常。如果功能比较复杂,需要分批整合,测试各个逻辑单元是否能正常工作。在这部分测试中,和外部环境交互的脚本如果逻辑较为简单,可以不参与,用模拟脚本替代。可以手动测试或自动测试。同样不能只测试正常情况。
@@ -30,4 +203,4 @@ Shell 脚本的最小测试粒度是单个脚本。必须保证单个脚本是
 
 ### 总结
 
-本文简单介绍了 shell 脚本的测试方法,以及编写可测试代码的方法。更多细节需要后续补充。
+本文简单介绍了 shell 脚本的测试方法,以及编写可测试代码的方法。