版權聲明:本文章原創於 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.csdn.net/taiyang1987912/article/details/39551385
https://www.runoob.com/linux/linux-shell-func.html