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

版权声明:本文章原创于 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 对应为函数第几个参数,此处 index1 开始,和一般语言中容器序号需要从 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 ,结束界面返回只需要点一个。

可以把每个界面的随机点击事件,都做成一个方法,根据传入数值,执行不同的点击按钮操作。而每个对应的命令使用点的位置也只在这里出现,后期维护也很容易。

说的有点绕,还是一步一步上代码。这里需要加入 shellcase 使用 与 随机生成函数

	
	#定义一个随机函数
	#用法 $(rand 0 100) 生成 0100 之间的数值(包括 0100)。
	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.csdn.net/taiyang1987912/article/details/39551385
https://www.runoob.com/linux/linux-shell-func.html

Android:
https://blog.bihe0832.com/adb-shell-input.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章