使用 Shell 编写 Android 自动化测试脚本

358 阅读8分钟

以前发在 CSDN 上, blog.csdn.net/u014443348/…

掘金氛围感觉更好一点,打算慢慢转过来。

使用 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 ;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:
blog.csdn.net/taiyang1987…
www.runoob.com/linux/linux…

Android:
blog.bihe0832.com/adb-shell-i…