使用 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

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