Sikuli-基於圖像識別的自動化測試框架


相關術語

縮寫 全稱 描述
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在執行的時候流程如下:

  1. 通過sikulixIDE,可以建立sikuli腳本,其中包括Python源代碼以及所需要的截圖。
  2. SikulixIDE執行腳本時,通過Python解析器和java庫的橋梁,核心部分解析是通過java庫實現的。
  3. 調用opencv在對截取的圖片進行比對搜索。
  4. 當搜索到對應的圖片后,會調用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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM