SHELL实现自动化测试框架Linux命令行测试
SHELL实现自动化测试框架(Linux命令行测试)
SHELL实现自动化测试框架
文章目录
相关术语
缩写 | 全称 | 描述 |
---|---|---|
SAT | Shell Automated Test | 使用Shell实现的自动化测试 |
背景
用以下功能用例举例
用例标题 | 步骤 | 预期 |
基础命令:验证which基础命令功能正确 | 1、在命令行输入:which bash | 1、输出:/usr/bin/bash |
2、在命令行输入:which ls whereis cp | 2、输出:/usr/bin/ls /usr/bin/whereis /usr/bin/cp |
手工测试,我们要执行该用例需进行4个步骤:
- 执行步骤1:命令行中输入命令
which bash
- 验证步骤1:查看步骤1输出结果,验证结果与用例中预期是否一致
- 执行步骤2:命令行中输入命令
which ls whereis cp
- 验证步骤2:查看步骤2输出结果,验证结果与用例中预期是否一致
模拟命令行中测试实例:
mars@mars-PC:~$ which bash # 步骤1
/usr/bin/bash # 步骤1输出结果
mars@mars-PC:~$ which ls whereis cp # 步骤2
/usr/bin/ls
/usr/bin/whereis
/usr/bin/cp # 步骤2输出结果
若从人工执行转换为自动化执行:自动执行命令、自动验证输出结果、生成测试报告,这样除了在全量测试时减少巨大的工作量,还能在回归测试、边缘测试时执行多覆盖大部分测试范围。
方案
实现该自动化方案,这里选择使用
Shell
来实现。
Shell
是一个命令解释器,它不仅是Linux操作系统内核与用户之间的绝缘层(俗称壳),同时也是一种功能相当强大的编程语言,可直接执行linux系统命令,它类似于DOS下的cmd.exe。
该方案除了Shell以外也可使用其他语言实现,比如Python,但是Python只能通过os、subprocess库来调用linux系统命令,不像Shell可直接执行系统命令,在脚本的编写和调式都不如Shell简洁、方便,所以最终方案的实现为Shell。这里对该技术方案命名为
SAT
方便后续说明使用。
使用场景【查询xx应用进程状态】的Shell、Python代码作为对比:
- Python
import subprocess
ps_state=subprocess.run('ps aux|grep -v grep|grep -q xx', shell=True).returncode
print(ps_state)
- Shell
ps aux|grep -v grep|grep -q xx
echo $?
通过以上对比可发现,实现一个相同的功能,Shell编写的代码要更为直接、简洁,命令越复杂这个优势越明显。
设计
SAT
是一个简单的自动化测试框架,主要实现命令行用例的自动执行并输出测试报告,体现出用例的执行情况,可快速对失败用例进行定位。总体可以划分为下面几个模块:
用例模块(case):存放转换为自动化测试用例的代码,最小单位为函数,该框架支持多个用例模块文件。
方法封装模块(method):存放一些公用方法函数,比如:断言、测试报告等。
启动测试模块(run_repor):存放启动测试的主代码,对用例模块文件进行批量处理并生成测试报告。
SAT目录结构如下:
SAT #主目录 ├── case # 存放用例模块的目录 │ ├── case_a.sh # 测试人员A用例文件 │ └── case_b.sh # 测试人员B用例文件 ├── method # 存放方法封装模块的目录 │ ├── assertion.sh # 断言函数封装模块文件 │ └── other.sh # 其他函数封装模块文件 ├── report # 存放测试报告的目录 │ ├── report_0817231130 # 测试报告文件 │ └── report_0819115441 └── run_report.sh # 启动测试模块文件
SAT框架运行流程图如下:
运行流程
步骤描述
依次调用函数
模块导入+变量初始化定义与赋值
依次执行导入的用例模块文件
统计用例通过与失败数量+失败用例编号
记录整个过程与结果落地归档
SAT
启动测试
环境初始化
执行测试用例
收集用例执行数据
生成测试报告
用例模块(case)
实际项目中每个人负责的模块与用例不同,所以自动化测试用例需要自己编写负责部分,不像其他模块中的代码都是公用的,那么在编写格式上需要做统一的约定:
- 用例模块文件命名统一,测试人员A、B的用例模块文件:case_a.sh、case_b.sh。
- 测试用例最小单位统一:每一条测试用例为一条函数,通过函数来管理用例。
- 用例函数命名统一,测试人员A的第1、2条用例:test_a1(){}、test_a2(){}。
- 用例函数元素统一,需要包含变量:title、case_id、断言函数。
格式统一之后,每人完成自己部分的用例文件,在启动测试时只要汇总全部用例模块文件,即可完成所有人的测试用例执行。
以下为测试人员A的用例模块文件 “case_a.sh” 部分内容:
#! /bin/bash
# 文件:case_a.sh
# 说明:基础镜像自动化测试用例
# 作者:黄先生
# 日期:2020/01/16
# 测试人员A的第一条用例
test_a1(){
title="检查内核启动信息" # 用例标题
case_id=xxxxx # 用例编号
a="error|fail|warning|call\ trace" # 用例执行命令结果
b=`sudo dmesg` # 用例预期结果
assertNoIn "${a}" "${b}" # 断言比较用例执行情况,若b中不包含a则通过
}
# 测试人员A的第二条用例
test_a2(){
pass
}
方法封装模块(method)
该模块主要用于存放公共方法函数,集中管理方便后期维护,封装的函数主要实现以下功能:
- 用例断言:判断用例执行结果与预期是否一致,给出对应输出结果。
- 用例处理:批量处理不同的用例模块文件:执行不同的用例模块文件,输出用例执行结果。
- 用例统计:统计用例执行的数据:统计执行用例数量、通过用例数量、失败用例数量、失败用例编号。
- 测试报告:结合以上全部内容输出本地文档,方便测试结果归档保存。
通过以上公共方法函数的封装,其他模块直接调用即可,代码更为清晰简洁,并减少冗余。
启动测试模块(run_repor)
该模块为执行测试的主模块,主要是对测试开始前环境做初始化并运行测试,主要包含:
- 模块初始化:导入方法模块;根据对用例模块文件的分析,导入对应的模块文件。
- 变量初始化:针对一些数据统计相关的函数做初始化赋值:用例数量、用例通过/失败数量、定义数组变量等。
- 运行测试:调用测试执行函数、测试报告生成函数、提示用户测试报告生成路径。
关键技术
这里主要选取了部分关键功能,其他部分则不细说了,针对这部分功能说明通过Shell是如何的实现,功能如下:
- 初始化
- 断言
- 测试执行
- 测试报告
初始化功能的实现
该功能是为后续测试提供良好的运行环境,主要分为两部分:
导入模块
- 首先会导入方法封装模块,然后判断存放用例模块的目录下是否有用例文件,根据判断结果对用例模块的导入和对应统计用例数量变量赋值。
初始化变量
- 对所有变量进行初始化定义与赋值,为后续的数据统计提供环境。
代码实现如下:
# 初始化函数 setup(){ if [ "`whoami`" != "root" ] then read -p "请已root用户运行该脚本,点击Enter退出" end exit fi source ./method/assertion.sh # 导入方法封装模块 source ./method/other.sh # 导入方法封装模块 if test -f ./case/case_a.sh # 判断是否存在用例模块 then source ./case/case_a.sh # 若存在用例模块则导入 num_a="`cat ./case/case_a.sh|grep test_a|wc -l`" # 若存在用例模块则获取用例数量并赋值给对应用例数量变量 else num_a=0 # 若不存在用例模块则对应用例数量变量赋值为0 fi if test -f ./case/case_b.sh then source ./case/case_b.sh num_b="`cat ./case/case_b.sh|grep test_b|wc -l`" else num_b=0 fi if test -f ./case/case_c.sh then source ./case/case_c.sh num_c="`cat ./case/case_c.sh|grep test_c|wc -l`" else num_c=0 fi fail=0 # 统计不通过用例数量 pass=0 # 统计通过用例数量 total=0 # 统计执行用例数量 declare -a fail_id # 定义失败用例ID数组变量 }
断言功能的实现
该功能实现的是在每一条用例执行之后,对用例执行结果与预期结果对比,判断用例执行结果为通过、失败,并且对结果进行输出;与此同时也参与了一些数据收集工作,递增通过/失败/执行用例数量。
断言对比的方法多种多样,这里拿部分举例,详情见下方代码:
# assertEqual ${1}等于${2}则为true
assertEqual(){
if [ "${1}" = "${2}" ] # ${1}、${2}变量为用例执行的过程中传入的执行结果与预期结果
then
r="pass" # 输出通过测试结果
else
r="fail" # 输出失败测试结果
fail_id[${fail}]=${case_id} # 提取结果为失败的测试用例ID,并添加进数组
fi
case_pf # 已封装函数:判断用例执行情况,操作对应数据统计变量,递增通过/失败/执行用例数量
}
# assertNoIn ${1}不在${2}中则为true
assertNoIn(){
result=`echo "${2}"|grep -E "${1}"`
if [ "${result}" ]
then
r="fail"
fail_id[${fail}]=${case_id}
else
r="pass"
fi
case_pf
}
测试执行功能的实现
该功能主要是实现不同用例模块中用例的批量执行,与此同时会辅助做一些其他工作:
- 创建测试报告文件,并初始化部分内容:记录测试开始时间、记录各测试模块中用例数量
- 实时输出结果至终端与测试报告文件
- 对输出结果进行排版设计
- 具体代码如下:
# 执行测试
test_start(){
t_start=$(date +"%m%d%H%M%S") # 获取当前时间戳并格式化
report="report_${t_start}" && report_path=./report/${report} && touch ${report_path} # 创建测试报告文件并赋值路径变量
# sl
# clear
echo "_____________________________________________________________________________" >> ${report_path} && cat ${report_path}
echo "" >> ${report_path} && cat ${report_path}| tail -1 # 此类追加格式为追加内容进测试报告并展示在终端
echo " # 测试开始 #" >> ${report_path} && cat ${report_path}| tail -1
echo " # 测试人员A:本轮测试,用例数量:${num_a}条 #" >> ${report_path} && cat ${report_path}| tail -1
echo " # 测试人员B:本轮测试,用例数量:${num_b}条 #" >> ${report_path} && cat ${report_path}| tail -1
echo " # 测试人员C:本轮测试,用例数量:${num_c}条 #" >> ${report_path} && cat ${report_path}| tail -1
echo "_____________________________________________________________________________" >> ${report_path} && cat ${report_path}| tail -1
echo "" >> ${report_path} && cat ${report_path}| tail -1
printf "%-20s %-20s%20s\n" 用例ID 执行结果 用例标题 >> ${report_path} && cat ${report_path}| tail -1 # 用例执行输出结果格式化
if [ "${num_a}" != "0" ]
then
for i in `seq 1 ${num_a}` # 批量执行测试人员A用例
do
test_a${i}
test_result;sleep 1 # 已封装用例执行输出结果函数,输出用例ID、测试结果、用例标题,并格式化排版
done
fi
if [ "${num_b}" != "0" ]
then
for i in `seq 1 ${num_b}` # 批量执行测试人员B用例
do
test_b${i}
test_result;sleep 1
done
fi
if [ "${num_c}" != "0" ]
then
for i in `seq 1 ${num_c}` # 批量执行测试人员C用例
do
test_c${i}
test_result;sleep 1
done
fi
}
测试报告功能的实现
该功能主要实现的是收集测试过程中与测试结束后的数据,并统一输出至文档文件中,并对每一次的测试结果做归档处理,方便后续跟踪某一次的测试结果。
具体实现代码如下:
# 测试报告统计通过、失败用例数量
case_pf(){
if [ "${r}" == "pass" ]
then
let pass+=1
elif [ "${r}" == "fail" ]
then
let fail+=1
fi
let total+=1
}
# 输出用例结果,并对结果进行格式化排版
test_result(){
printf "%-20s %-20s %-20s\n" ${case_id} ${r} ${title} >> ${report_path} && cat ${report_path}| tail -1
}
# 测试报告结束
test_report(){
t_end=$(date +"%m%d%H%M%S") # 获取测试结束时间
echo "_____________________________________________________________________________" >> ${report_path} && cat ${report_path}| tail -1
echo "" >> ${report_path} && cat ${report_path}| tail -1
echo "测试开始时间:${t_start}" >> ${report_path} # 依次输出各关键信息
echo "测试结束时间:${t_end}" >> ${report_path}
echo "测试电脑架构:`uname -m`" >> ${report_path}
echo "执行用例合计:${total}" >> ${report_path}
echo "用例通过数量:${pass}" >> ${report_path}
echo "用例失败数量:${fail}" >> ${report_path}
echo "失败用例ID:${fail_id[*]}" >> ${report_path}
cat ${report_path} | tail -7
echo "_____________________________________________________________________________" >> ${report_path} && cat ${report_path}| tail -1
}
实验验证
为了说明
SAT
的工作流程,这里拿一个实际场景举例,本次测试需要自动化执行:测试人员A、测试人员B的用例,无需执行测试人员C的用例:
操作流程如下
- 收集测试人员A、测试人员B最新的测试用例模块文件,放入目录"SAT/case/“下。
- SAT目录下启动终端,运行命令
su
进入Root。- 因部分命令需要root权限,使用sudo+命令需要提示用户输入密码,不友好。
- 若用户使用普通用户权限运行,则给出对应提示:
请已root用户运行该脚本,点击Enter退出
,并关闭。
- 终端实时查看测试过程,过程中会输出用例数据与用例执行结果。
- 等待测试完成后,终端可查看测试结果,重点关注失败用例ID,对其做二次验证并提交Bug单。
- 关闭终端后若还需查看测试结果,进入"SAT/report/“目录,根据文件命名中时间戳定位测试报告文件并查看。
终端中与测试报告中内容展示一致,主要体现自动化测试流程与结果,可看到下方输入内容,展示各关键信息了:
_____________________________________________________________________________
# 测试开始 #
# 测试人员A:本轮测试,用例数量:5条 #
# 测试人员B:本轮测试,用例数量:1条 #
# 测试人员C:本轮测试,用例数量:0条 #
_____________________________________________________________________________
用例ID 执行结果 用例标题
xxxxxx pass 检查内核信息与版本号
xxxxxx fail 检查内核启动信息
xxxxxx pass 验证perl环境
xxxxxx pass 验证python环境
xxxxxx pass 验证系统预装软件包状态
xxxxxx fail 创建test文件夹
_____________________________________________________________________________
测试开始时间:0818102422
测试结束时间:0818102428
测试电脑架构:x86_64
执行用例合计:6
用例通过数量:4
用例失败数量:2
失败用例ID:xxxxxx xxxxxx
_____________________________________________________________________________
# 测试完毕,测试报告生成在'./report'目录,点击Enter退出! #
小结
如此一个简单的自动化测试框架就完成了,已经可以很好的满足日常测试,以上是初期的一个简单设计。经过后续的迭代,目前已经支持各种功能配置、环境检测、测试流程控制、邮件发送测试报告、自动代码提交、代码格式/规范扫描等。大家可根据自己的项目需要做相应的完善,这里仅提供一个思路。