相關術語
縮寫 | 全稱 | 描述 |
---|---|---|
Accessibility | 輔助功能 | 通過應用提供的Accessibility特性可以定位到相應的元素 |
IDE | Integrated Development Environment | 集成開發工具 |
sikulixIDE | sikulixIDE | Sikuli自帶的集成開發工具 |
問題
在UOS操作系統桌面應用的GUI自動化測試中,通常的解決方案是對桌面應用添加Accessibility,然后通過Dogtail或LDTP這類工具去獲取應用的元素,從而可以對元素進行定位和操控。
UOS操作系統桌面應用在開發的時候,並沒有添加Accessibility,那么我們現在要通過這種方法去做自動化測試,只能讓開發人員重新去對各個應用的控件去添加Accessibility,以便於我們開展自動化測試。
那么,桌面應用自動化測試除了通過獲取元素的屬性來定位和操作以外,有沒有不依賴於Accessibility的自動化測試方案呢?
現狀
目前,我們針對Linux操作系統桌面應用進行自動化測試時,使用Accessibility來定位和操作應用元素,Accessibility(輔助功能)在很多操作系統中都存在,比如windows、IOS、Android。
這個實現功能實現的初衷是讓殘疾人士也可以使用操作系統,就像freedesktop官網關於Accessibility是這樣描述的:"世界人口的15%生活在某種形式的殘疾中,輔助功能對很多用戶很重要,沒有它,他們就是不能使用他們的電腦。"
Accessibility有幾個重要原則要牢記在心
- 我們希望使現有軟件可訪問,並避免專用軟件
- 我們需要同步:可訪問性只是輸入和輸出的另一種方式
- 它應該很容易獲得,隨時可以啟用
因此,基於Accessibility的這種可訪問性的特性,就可以輔助我們做自動化測試。
但具體到應用,就要看應用在編碼的時候是否加了Accessibility的屬性,如果沒有添加Accessibility,則無法被識別到,后期去添加需要耗費更多的人力成本和時間成本。
技術方案
Sikuli是麻省理工學院的一個開源項目,一種新穎的圖形腳本語言,它是一個基於圖像識別的GUI自動化測試框架,底層是基於opencv實現對圖像的識別,使用者只需要會最簡單的編程技能,就能輕松的使用它。
Sikuli 在墨西哥維喬印第安人的語言里是上帝之眼的意思,所以被稱為“上帝之眼”。與通常所見的自動化測試框架不同,Sikuli不需要應用添加任何的屬性,僅通過圖像就可以對元素進行定位和操作,所謂“所見即所得”,只要眼睛能夠看到的,sikuli就能夠識別到,無論是web端、App端、桌面端,都可以輕松實現跨平台的自動化測試,具有很強的兼容性。
整體設計
sikuli分為三個模塊:
- sikulixIDE
sikulixIDE是在.sikuli
結尾的目錄中編寫sikuli的腳本,以及編輯png格式的圖片,然后在java環境中,使用Jython執行sikuli的腳本。
- 以
.sikuli
結尾的目錄
目錄中保存有sikuli的腳本,以及png格式的圖片,其中腳本是py文件,圖片可以有兩種形式截取,一種是通過sikulixIDE工具提供的截圖功能直接截圖,截圖之后默認保存的名稱是隨機數字,當然我們可以在文件管理器中將圖片名稱進行修改,另一種是通過三方工具截取png格式的圖片,命名可以自定義。
- sikuli腳本
sikuli腳本為.sikuli
目錄下的py文件,一個目錄下只有一個py文件,一般編寫腳本的時候是在IDE工具里面進行編寫,在熟悉sikuli的語法之后,也可以采用其他的編碼工具進行編寫,只要主要圖片名稱與代碼的關聯關系即可。
sikuli在執行的時候流程如下:
- 通過sikulixIDE,可以建立sikuli腳本,其中包括Python源代碼以及所需要的截圖。
- SikulixIDE執行腳本時,通過Python解析器和java庫的橋梁,核心部分解析是通過java庫實現的。
- 調用opencv在對截取的圖片進行比對搜索。
- 當搜索到對應的圖片后,會調用java.awt.Robot控制鼠標和鍵盤事件,從而實現相應的操作。
關鍵技術
環境
在UOS系統中搭建以下環境:
Java環境
由於sikuli調用的opencv的Java API,所以依賴Java環境
sudo apt-get install openjdk-8-jre
安裝opencv
sudo apt-get install libopencv3.2-java
sudo ln -s /usr/lib/jni/libopencv_java320.so /usr/lib/libopencv_java.so
安裝tesseract
sudo apt-get install tesseract-ocr
sudo apt-get install libtesseract-dev
sudo apt-get install libleptonica-dev
下載sikulixIDE
wget https://launchpadlibrarian.net/469010975/sikulixide-2.0.4.jar
在Sikuli的IDE工具中,可以對腳本進行開發。
下載jython
wget https://repo1.maven.org/maven2/org/python/jython-standalone/2.7.1/jython-standalone-2.7.1.jar
jython實現了使用Python語言調用java的功能。
運行IDE
將sikulixIDE和jython放在統一目錄下,然后切換到這個目錄下,
在終端輸入:java -jar sikulixide-2.0.4.jar
即可啟動sikuli的IDE工具,然后就可以在IDE中進行腳本編寫
點擊
Click()
將光標定位到括號內,使用IDE工具提供的截圖功能,截取我們想要點擊的圖標,圖片就會自動顯示在括號內
如果是使用其他工具截取的圖片,只需要括號內直接輸入圖片的路徑即可(格式為png)。
比如:
Click(computer.png) # 運行之后鼠標會去點擊“我的電腦”圖標
雙擊
doubleClick()
使用方法和Click()類似,使用IDE工具直接截圖,或輸入圖片的路徑。(以下沒做說明的,都是采用這種方法)
比如:
doubleClick(computer.png) # 運行之后鼠標回去雙擊“我的電腦”圖標
右鍵點擊
rigthClick()
拖拽
dragDrop(png1,png2) # from png1 to png2
括號內寫兩個圖片的路徑,表示從png1拖拽到png2的位置。
檢查是否存在
exists(png)
這個方法通常用於斷言
# 判斷圖片是否存在
if exists(png1):
print("png1存在")
else:
print(png1不存在)
睡眠
sleep() # sleep(1)
括號內寫睡眠的時間,單位是秒,這個方法類似於python里面,time.sleep()表示腳本運行時,暫定幾秒的時間。
輸入內容
type("text")
括號內寫要輸入的文本內容。
鍵盤
控制字符
type(Key.ENTER) # 表示按回車鍵
括號內是Key加上大寫的控制字符,常見的ENTER, TAB, ESC, BACKSPACE, DELETE, INSERT等等
快捷鍵
type(“ c”,Key.CTRL) # 表示Ctrl + c
括號內是控制字符的組合按鍵,常見的ALT, CMD, CTRL, SHIFT, WIN等等,其中三個按鍵的情況要特殊說明下,
type(Key.ESC,Key.CTRL + Key.SHIFT) # 表示Ctrl + Shift +ESC
運算符
運算符與python里面使用方法相同,+, - ,*, /, <, >, =,and,or,not等等,這里不做展開說明。
實驗驗證
以相冊為例
編寫相冊的用例腳本:
1.封裝基礎方法
定義寫用例之前要用到的方法
import time
import os
import getpass
times = time.strftime("%Y_%m_%d %H:%M:%S")
username = getpass.getuser()
#===================================================================================
# 定義用例操作步驟所要用到的方法
#===================================================================================
# 定義寫日志的方法
def report(txt):
print(txt)
file = "./report/album_report_%s.report" % times
with open(file,"a") as f:
f.write(txt + "\n")
# 如果存在圖標,打印log_t,如果不存在圖標,打印log_f,(通常用於斷言)
def if_exists(pic, log_t, log_f):
sleep(0.3)
log_f = log_f + "====================Fail"""
# log_f后面加的Fail是為了在日志文件中能夠重點體現出來,一眼就可以看到哪些用例失敗
if exists(pic,3):
report(log_t)
else:
report(log_f)
# 如果不存在圖標,打印log_t,如果不存在圖標,打印log_f
def if_not_exists(pic, log_t, log_f):
sleep(0.3)
if not exists(pic):
report(log_t)
else:
report(log_f)
# 如果存在圖標,則點擊圖標,如果不存在,則打印報錯信息。
def find_and_click(pic, log_f):
sleep(0.3)
log_f = log_f + "====================Fail"
if exists(pic,3):
click(pic)
else:
report(log_f)
# 雙擊圖標
def find_and_double_click(pic, log_f):
sleep(0.3)
log_f = log_f + "====================Fail"
if exists(pic,3):
doubleClick(pic)
else:
report(log_f)
# 右鍵單擊圖標
def find_and_right_click(pic, log_f):
sleep(0.3)
log_f = log_f + "====================Fail"
if exists(pic,3):
rightClick(pic)
else:
report(log_f)
# 在桌面空白處右鍵點擊
def right_click_on_desktop(jsj = "1596177723547.png"):
sleep(0.3)
if exists(jsj):
btn = find(jsj).right(800)
rightClick(btn)
sleep(0.3)
else:
report("計算機圖標不存在!")
# 關閉窗口
def close_window():
#find_and_click(Pattern("1596524278245.png").targetOffset(89, -3), "窗口關閉失敗")
if exists("1596524278245.png"):
click(Pattern("1596524278245.png").targetOffset(89, -3))
# 關閉所有窗口
def close_all_window():
n = 0
while n < 3:
if exists("1596524278245.png"):
close_window()
n = n + 1
else:
report("環境清理:關閉所有窗口")
break
# 所有窗口最小化
def min_window():
#if_exists_and_click(Pattern("1596524278245.png").targetOffset(-12,0),"最小化窗口失敗")
if exists("1596524278245.png"):
click(Pattern("1596524278245.png").targetOffset(-12,0))
# 最小化所有窗口
def min_all_window():
while True:
if exists("1596524278245.png"):
min_window()
else:
report("環境清理:最小化所有窗口")
break
# 命令行執行的方法
def cmd(doit):
os.system(doit)
# 刪除桌面文件
def delete_desktop_file(format):
cmd("rm /home/%s/Desktop/*.%s" % (username,format))
# 殺進程
def kill_process(process):
cmd("ps -ef | grep %s | grep -v grep | cut -c 9-15 | xargs kill -9" % process)
# 從任務欄打開相冊
def open_album():
find_and_click(Pattern("1596610577797.png").targetOffset(-2,5),"相冊應用圖標未找到")
#===================================================================================
2.編寫用例腳本
用例的腳本實際上是基於之前封裝好的方法,傳入相應的圖片文件,以及斷言的文本即可。
def test_album_1():
report("用例001:外部調用相冊應用")
find_and_double_click("1596609261881.png","主目錄沒找到")
sleep(1)
find_and_click("1596609375609.png","圖片目錄未找到")
sleep(1)
if exists("1596609484192.png"):
find_and_double_click("1596609484192.png","Wallpapers圖標沒找到")
sleep(1)
elif exists("1597228353694.png"):
find_and_double_click(Pattern("1597228353694.png").targetOffset(48,58),"Wallpapers圖標沒找到")
sleep(1)
find_and_right_click("1596609522418.png","圖片未找到")
find_and_click("dakaifangshi.png","打開方式選項未找到")
find_and_click("yixiangce.png","相冊選項未找到")
# 斷言
if_exists("1596609775470.png","外部調用相冊成功","外部調用相冊失敗")
# 關閉打開的窗口
kill_process("deepin-album")
close_all_window()
可以看到,用例腳本里面大多都是傳入的圖片名稱,圖片名稱為數字的,例如:1596609261881.png,是使用sikulixIDE工具提供的截圖功能直接截圖的,默認是保存到當前目錄下,而使用單詞命名的是通過三方截圖工具截取的,例如:dakaifangshi.png,我們將圖片放到當前目錄下就可以被識別到。
如果我們在截圖圖片的時候弄亂了,當前目錄下存在腳本里面沒有被用到的圖片,Sikuli的IDE工具會自動檢測,將沒用的圖片刪除。
3.執行的腳本
由於sikulixIDE工具沒有提供組織用例的功能,在一個py文件中,我們定義了多個用例,但是在執行的時候,可能只需要執行部分的用例,那么我們就需要編寫組織用例的方法。
doit = range(1,2)
for i in doit:
i = str(i)
eval("test_album_%s()" % i)
這里面用到python里面的eval函數,就是將字符串轉換成腳本來執行,我們組裝成一個用例的方法名,即可實現用例的執行,在for循環中,我們用doit這個列表在組裝要測試的用例,這里我用的是range函數,也可以在一個列表中定義要執行的用例序號。
小結
優點:
- Sikuli在UOS操作系統的環境搭建比較簡單,所有元素控件均以圖片的形式進行保存,不依賴於應用的屬性。
- 在sikuliIDE中使用簡單的編程語法,就能實現對自動化測試腳本進行編寫,語法簡單易懂,即使時不懂編程的人,也能快速上手,編寫自己的自動化測試用例。
- IDE中提供了截圖的功能,截圖后可以直接在代碼中顯示。
- 可以設置圖片的相對位置,方便我們定位相對目標位置的任意位置。
- 不對應用的安全性造成應用,可以實現測試環境與開發環境的隔離。
缺點:
- 所有元素控件均以圖片的形式進行保存,用例較多時,需要保存大量的圖片,項目會比較臃腫。
- 后期維護性比較差,需要修改圖片,重新截圖等,比較容易亂,通過IDE自帶的截圖工具截取的圖片,名稱為隨機數,從名稱上不容易識別。
- IDE沒有自動生成測試報告的能力,需要在代碼中自己實現。
- IDE報錯模糊,不能很好的定位代碼問題,如果要封裝一些方法,需要特別注意,這點來講就需要有較強的編碼能力。
- IDE沒有補全代碼的功能,易用性差。
- 批量執行方便,IDE中不提供批量執行的功能,在命令行中執行時也不能進行測試用例的組織。
- 無法和其他框架配合使用,也不能導入三方模塊。
綜上,sikuli是基於圖像識別的自動化測試方案,腳本語法簡單,可以簡單快速的編寫測試用例,即使是初級工程師也可以輕松的使用並編寫測試腳本,可以用於UOS桌面應用的自動化測試。
參考文檔
sikuli官方文檔:https://sikulix-2014.readthedocs.io/en/latest/region.html
sikuli使用文檔:
https://sikulix-2014.readthedocs.io/en/latest/region.html#Region.exists
https://sikulix-2014.readthedocs.io/en/latest/region.html#lowlevelmouseandkeyboardactions