版权声明:本文章原创于 RamboPan ,未经允许,请勿转载。
使用 Shell 编写 Android 自动化测试脚本
最近碰到一个需求:就是需要反复测试一个简单的应用,是否能稳定的长期运行。
这个应用是类似一个答题过程,选择一个难度,然后选择正确的答案,然后接下来回答下一轮,答题完成会弹出结果页面,最后再自动或手动退回主界面。
因为需要点击固定的位置,反复的进行点击。刚开始测试这个应用就类似自己玩一下,感觉还有点意思。后面发现老改动,老自己点,这 …… 一点都不符合程序员的准则是吧:能自动化的就不要自己动手。
想到命令行中也能通过输入命令模拟对屏幕触控 … 那第一反应就是能不能编一个脚本,把要执行的命令与逻辑做好,脚本不断使用命令来达到模拟触屏的实现。
查阅了 Mac 平台使用批处理的大概思路。参考了一些 Shell 文档(文末附上链接),经过几天调试优化,感觉效果还算可以,拿出来分享下,也算是一个自动化测试思路吧。
先放个效果图。
首先分析这个应用的流程:
主界面:
有四个选项框,选择不同的难度。
答题界面:
虽然中间的图像部分会因为难度变化而变化,但下面四个选项位置不会改变,所以每次点击都是4个位置中的固定位置。
结果界面:
中间有可以滑动的列表,显示着每次答题的正确答案。如果点击右上角可以手动返回主菜单,如果不点击,则自动返回主菜单。
这里我们先按照简单的实现,就是一直答题,答题结束后,再返回主菜单继续。
说完了流程,我们来熟悉接下来需要使用到 Shell 中的操作。
(使用的是 Mac 系统,如果使用 Windows 系统,略有差别。)
我们新建一个文本文件,第一行输入
#!/bin/bash
指定解释的路径,然后将后缀改为 .sh。
按着新语言的学习过程,肯定是要来一个 HelloWorld 的,那我们再下一行输入
echo Hello,World ~
如果命令行当前位置与文件位于同一个目录,则可以直接
./test1.sh(此处 test 是我的文件名)执行当前目录下 test.sh 脚本。
如果不在同一个目录,将文件拖入命令行中就可以了。
是不是满怀期待,发现居然是 Permission denied ,惊不惊喜 ~
我们输入 chmod 777 加上刚刚的脚本名,给指定脚本权限,例如:
chmod 777 /Users/RamboPan/Desktop/test1.sh
#输出:
Hello,World
这次 Hello,World 如期出现。
echo 作用,有许多,这就说两个作用:作为一个输出指令对字符串进行输出,或者函数的返回值。返回值一会看到。剩下依次将使用到的部分做个简单示例,需要深入了解的可以查看对应 Shell 文档。
四则运算(除法和 int 除法类似):
#代码:
a=40;
b=30;
result=`expr $a + $b`
echo "a + b : $result"
result=`expr $a - $b`
echo "a - b : $result"
result=`expr $a \* $b`
echo "a * b : $result"
result=`expr $a / $b`
echo "a / b : $result"
result=`expr $a % $b`
echo "a % b : $result"
输出:
a + b : 70
a - b : 10
a * b : 1200
a / b : 1
a % b : 10
流程控制:If
#if 使用
if (( 3 < 2 ))
then
echo "3 < 2"
else
echo "3 > 2"
#需要使用 fi 进行结束说明
fi
输出:
3 > 2
while
#while 使用
index=1;
while (( $index < 5))
do
echo "It's $index"
index=`expr $index + 1`
done
输出
It's 1
It's 2
It's 3
It's 4
case (switch)
#case 使用 (switch)
case 1 in
1) echo "It's 1" ;;
2) echo "It's 2" ;;
esac
输出
It's 1
方法定义与调用
#方法定义
function sayHello(){
echo "sayHello"
}
$(sayHello)
#输出
sayHello
function tap(){
adb shell input tap $1 $2
}
$(tap 100 100 )
#输出 (这里我没插设备,所以没检测到)
no devices/emulators found
大概熟悉了下 Shell 之后,就开始组建测试的语句。
我们先来模拟下点击和滑动的指令。
//滑动 (点 0,0 滑动到 点100,100 ) 处。
adb shell input swipe 0 0 100 100
//点击(点 0,0)
adb shell input tap 0 0
我们连上 Android 设备之后,在命令行(终端)中输入滑动那句命令,因为先测试点击屏幕不容易看出效果,我们先测试滑动,从顶端划出菜单栏。
这效果很明显。此时我们在命令行中输入点击的命令,如果执行没有问题的话,刚刚弹出的菜单栏会收回,(这里点到了广告 -_- #),因为我们点在菜单栏的外面了。(部分手机的菜单栏长度各有不同,根据自己设备调整)
这样两个命令执行就没什么问题了。
因为会执行各种点位的点击与滑动,那么我们需要把点击命令的前半段,就是 英文部分 和 位置部分 拆开处理。
点击点的位置基本不动,所以此处也需要做一些变量来存储这些固定的位置。
既然都已经拆开了,那我们肯定第一想到就是做两个方法:点击某个位置,从哪滑动到哪。
先放下代码。
#点击前半段
baseTap="adb shell input tap";
#滑动前半段
baseSwipe="adb shell input swipe";
#点击的实现
function tapPoint(){
$baseTap $1 $2
}
#滑动的实现
function tapAnyWhere(){
$swipe $1 $2 $3 $4
}
因为 shell 中引用变量需要加 $ 符号,使用 bastTap 变量就是 $baseTap ;$1 $2 对应为函数第几个参数,此处 index 从 1 开始,和一般语言中容器序号需要从 0 开始不一样。
然后我们再添加一些固定的点。
#界面1 左边点的x
xl=xxx;
#界面1 右边点的x
xr=xxx;
#界面1 上边点的y
yu=xxx;
#界面1 下边点的y
yd=xxx;
#如果要点击第一个界面,左上方的 简单模式 按钮,那么我们就使用函数传入对应参数来调用。
#因为左上角就是左x,上y的点。
$(baseTap $xl $yu)
#从左上到右下的滑动
$(baseSwipe $xl $yu $xr $yd)
看着好像还可以,不过现在有一些问题。参数多了倒是还能执行,如果少些参数就没办法正确执行。
而且如果整个文件都是使用这个函数,那么如果中途要改点什么需要改动就太大了,维护成本极高,所以我们还要继续进行封装:
我们点击的过程中,需要模拟随机点击哪个按钮:比如主界面,需要 4 选 1 ,答题界面需要 4 选 1 ,结束界面返回只需要点一个。
可以把每个界面的随机点击事件,都做成一个方法,根据传入数值,执行不同的点击按钮操作。而每个对应的命令使用点的位置也只在这里出现,后期维护也很容易。
说的有点绕,还是一步一步上代码。这里需要加入 shell 的 case 使用 与 随机生成函数:
#定义一个随机函数
#用法 $(rand 0 100) 生成 0 到 100 之间的数值(包括 0 与 100)。
function rand(){
min=$1
max=$(($2-$min+1))
num=$(($RANDOM))
#此处的 echo 就负责返回结果
echo $(($num%$max+$min))
}
#定义屏幕一的随机点击方法,
function randomTapS1(){
#因为四选一,所以此处直接使用 $(rand 0 3)
index = $(random 0 3);
#根据 index 的值,走不同的选项。
case $index in
#点击屏幕1左上方按钮
0) $(tapPoint $xl $yu) ;;
#点击屏幕1右上方按钮
1) $(tapPoint $xr $yu) ;;
#点击屏幕1左下方按钮
2) $(tapPoint $xl $yd) ;;
#点击屏幕1右下方按钮
3) $(tapPoint $xr $yd) ;;
#使用了 case 需要使用 case 的反向(esac)结束选择。
esac
#此处加入 echo 对 index 返回,调用方法可以获取结果进行输出。
echo $index;
}
既然能模拟出第一个界面的点击事件,那么第二个、第三个界面的点击事件,也可以依次做出来。这里就直接跳过了。
接下来就是整个应用的模拟点击流程编写了。
#先来定义个当前测试轮数与总测试轮数.
startCount=0;
totalCount=100;
#当不满足总轮数时一直循环
while((startCount<totalCount))
do
#输出日志,当前是第几轮开始。
echo "Now start play round : $startCount"
#choose mode main menu
cIndex=$(randomTapS1);
#输出日志,第一个界面选择的是什么模式(哪个按钮)
echo "Choose Mode : $cIndex";
#等待一秒钟
sleep 1
#因为有4个问题,所以需要做一个小循环。
#start inner loop
i=0;
j=5;
while((i<j))
do
cIndex=$(randomTapS2);
#输出日志 当前点击的是第几个选项
echo "Choosing: Round: $i ,choose index : $cIndex";
#i++;
i=`expr $i + 1`;
sleep 1
#退出小循环
done
#点击返回按钮
$(randomTapS3);
#输出日志 返回主菜单
echo "Click back to main menu";
sleep 1
#测试轮数+1
startCount=`expr $startCount + 1`;
done
因为流程比较简单,加上有注释说明,应该不难看懂。
既然现在测试可以运行了,那么就有一个所有 App 都需要考虑的问题,适配。这些对应的按钮在不同的设备上,肯定因为分辨率不同,坐标也不同。
当然可以只在一台固定的设备上测试,比如 1920 x 1080,不过如果要在其他设备上测试的话,肯定是要用个比例与基准尺寸进行运算的。
浮点运算在 shell 中稍微有点麻烦,我搜了一个实现,然后也简单封成方法。
#计算两个数相乘
function multiDouble(){
#保留两位小数精度。
echo "scale=2;$1 * $2" | bc;
}
把用到的对应坐标都换成 比例 与对应 基准尺寸 就行。
#base screen size
baseWidth=1920;
baseHeight=1080;
#之前用的 屏幕1 对应4个点的 x y 坐标,后面统一换成比例乘以基准尺寸。
#当然基准尺寸可以考虑做成运行脚本时的参数,有兴趣的可以试下。
#screen 1
# xl=780;
# xr=1150;
# yu=640;
# yd=800;
xl=$(multi 0.4 $baseWidth);
xr=$(multi 0.6 $baseWidth);
yu=$(multi 0.6 $baseHeight);
yd=$(multi 0.74 $baseHeight);
现在又想到一个问题,如果 …… 有多比例需要考虑(相乘)怎么办(真的是过场有点多哈 ~ (→_→) ),那么我们可以考虑 for 循环来实现。
#计算两个数相乘
function multiDouble(){
#保留两位小数精度。
echo "scale=2;$1 * $2" | bc;
}
#计算多个数相乘,判断传入参数个数多少计算不同计算
function multi(){
#没有参数时返回0
if [ $# = 0 ]
then
echo 0
#一个参数时返回该参数
elif [ $# = 1 ]
then
echo $1
#两个参数时返回乘积
elif [ $# = 2 ]
then
echo $(multiDouble $1 $2)
#多个参数时使用循环,声明一个结果,循环轮乘
else
result=1.0;
for var in $@
do
#echo "It's $(($index))"
result=$(multiDouble $result $var)
done
echo $result
fi
}
######################
#调用示范:
echo $(multi)
echo $(multi 1.1)
echo $(multi 1.1 2.2)
echo $(multi 1.1 2.2 3.3 )
echo $(multi 1.1 2.2 3.3 4.4 )
输出结果:
0
1.1
2.42
7.98
35.11
这样使用 multi 函数,不管多少参数都可以正确使用,真是机智 : ) 。
最后放一个完整版的代码,程序不一样,也只是仅供参考。
#!/bin/bash
#define a ramdom funtion
function rand(){
min=$1
max=$(($2-$min+1))
num=$(($RANDOM))
echo $(($num%$max+$min))
}
#define a tap anywhere function
function tapAnyWhere(){
$baseTap $(rand 0 $baseWidth) $(rand 0 $baseHeight);
}
#define a function to tap specify point
function tapPoint(){
$baseTap $1 $2
}
#define a random tap button function in screen1
function randomTapS1(){
index=$(rand 0 3);
case $index in
0) $(tapPoint $xl $yu) ;;
1) $(tapPoint $xr $yu) ;;
2) $(tapPoint $xl $yd) ;;
3) $(tapPoint $xr $yd) ;;
esac
echo $index;
}
#define a random tap button function in screen2
function randomTapS2(){
index=$(rand 0 3);
case $index in
0) $(tapPoint $x1 $y1) ;;
1) $(tapPoint $x2 $y1) ;;
2) $(tapPoint $x3 $y1) ;;
3) $(tapPoint $x4 $y1) ;;
esac
echo $index;
}
#define a random tap button function in screen2
function randomTapS3(){
$(tapPoint $xb $yb);
}
#define a swipe anywhere function
function swipeAnyWhere(){
$baseSwipe $(rand 0 $baseWidth) $(rand 0 $baseHeight) $(rand 0 $baseWidth) $(rand 0 $baseHeight);
}
#define a center swipe in result page
function swipteCenter(){
$baseSwipe $(rand $xStart $xEnd) $(rand yStart $yEnd) $(rand $xStart xEnd) $(rand yStart $yEnd);
}
#define two float multi
function multiDouble(){
echo "scale=2;$1 * $2" | bc;
}
#define several float multi
function multi(){
if [ $# = 0 ]
then
echo 0
elif [ $# = 1 ]
then
echo $1
elif [ $# = 2 ]
then
echo $(multiDouble $1 $2)
else
echo $(multi $1 $(multiDouble $2 $3))
fi
}
function addDouble(){
echo "scale=2;$1 + $2"|bc;
}
function add(){
if [ $# = 0 ]
then
echo 0
elif [ $# = 1 ]
then
echo $1
elif [ $# = 2 ]
then
echo $(addDouble $1 $2)
else
echo $(add $1 $(addDouble $2 $3))
fi
}
startCount=0;
totalCount=100;
#screen 1:fou button:
#leftTop 780 640
#rightTop 1150 640
#leftBottom 780 800
#rightBottom 1150 800
#screen 2:four button:
#key1:475 1024
#key2:730 1025
#key3:1010 1024
#key4:1277 1024
#sreen 3:back button
# 1815 110
#base screen size
baseWidth=1920;
baseHeight=1080;
#screen 1
# xl=780;
# xr=1150;
# yu=640;
# yd=800;
xl=$(multi 0.4 $baseWidth);
xr=$(multi 0.6 $baseWidth);
yu=$(multi 0.6 $baseHeight);
yd=$(multi 0.74 $baseHeight);
#screen 2
# x1=475;
# x2=730;
# x3=1010;
# x4=1277;
# y1=1024;
x1=$(multi 0.25 $baseWidth);
x2=$(multi 0.38 $baseWidth);
x3=$(multi 0.52 $baseWidth);
x4=$(multi 0.25 $baseWidth);
y1=$(multi 0.95 $baseHeight);
#screen 3
# xb=1815;
# yb=110;
xb=$(multi 0.945 $baseWidth);
yb=$(multi 0.1 $baseHeight);
#center rect
xStart=$(multi 0.156 $baseWidth);
xWidth=$(multi 0.71 $baseWidth);
xEnd=$(add $xStart $xWidth);
yStart=$(multi 0.28 $baseHeight);
yHeight=$(multi 0.49 $baseHeight);
yEnd=$(add $yStart $yHeight);
#base input cmd
baseTap="adb shell input tap";
baseSwipe="adb shell input swipe";
#start out loop
while((startCount<totalCount))
do
echo "Now start play round : $startCount"
#choose mode main menu
cIndex=$(randomTapS1);
echo "Choose Mode : $cIndex";
sleep 1
#start inner loop
i=0;
j=5;
while((i<j))
do
cIndex=$(randomTapS2);
echo "Choosing: Round: $i ,choose index : $cIndex";
i=`expr $i + 1`;
sleep 1
done
$(randomTapS3);
echo "Click back to main menu";
sleep 1
startCount=`expr $startCount + 1`;
done
如果有疑问或者意见,欢迎指出,共同讨论。
参考文章:
Shell:
https://blog.youkuaiyun.com/taiyang1987912/article/details/39551385
https://www.runoob.com/linux/linux-shell-func.html