Appium iOS 測試指令碼開發實戰

筆尖哇發表於2021-01-01

最近老闆家心情好,不停的給UI加了好多的任務,iOS開發部門的小哥哥見到UI妹妹的眼神由愛轉嫌棄,飽含深深地怨氣,每個周都會釋出一個迭代;迴歸測試就是一個繁瑣又不能忽略的事,沒辦法就是懶,不想繼續點點點,乾脆寫個弱弱的iOS測試指令碼,滿足下自己懶得慾望,一番調研,選擇了Appium作為iOS的測試框架,當然我沒找到更合適的框架了;一週小成,分享出來,開心開心,大佬屠刀輕點,同為菜鳥慢慢吐槽?

首先,開發iOS測試指令碼的最重要前提是你的有一臺macOS系統的裝置

安裝jdk,弱弱的問一句,哪位和開發沾點邊的電腦沒有jdk;Java -version檢查下吧

接下來就是安裝node.js,有好多寫的很好的博文,不懂的可以移步瞭解下,檢查下您家電腦的node是否安裝成功吧

附帶的,檢查下您家電腦的npm是否可以正常使用吧

安裝完成了以上的幾個小東西,接下來就是安裝我們尊貴的Appium了,Appium有兩種,一種是桌面版,一種是命令列模式,這裡建議安裝桌面版的Appium-desktop,附上下載連結,下載後裝即可;

https://github.com/appium/appium-desktop/releases

安裝完成即可啟動

僅僅是安裝完成Appium-desktop可不代表可以您能開始的秀上您的優雅程式碼,我這這個坑硬是填了整整一天,Appium-desktop始終無法和客戶端進行通訊,一直髮生各種各樣的報錯,抑鬱症狂犯;

到這裡,先需要檢查下Appium是否安裝了它所需要的類庫,最好的方式就是使用其自帶的appium-doctor檢查下。

在命令列客戶端中輸入appium-doctor,appium-doctor就會自動檢查類庫的安裝是否完整,當沒安裝完整是就會在圖中的紅框部分顯示出來,這時你需要根據提示將缺失的類庫一一安裝,直到不再爆紅(以上爆紅為安卓測試需要用到的adb和sdk,這裡測試iOS,不進行處理)

到這裡,Appium-desktop的安裝完成,看看他的盛世美顏吧

要想和手機進行通訊,最重要的一環是安裝WebDeiverAgent,安裝WebDeiverAgent可能需要iOS開發同事的幫忙,但是你如果很瞭解xcode的使用情況下,可以自行下載,並完成編譯,這部分的安裝我暫且不做介紹,因為我之前也是根據大神的教程來完成安裝的,需要的可以參考大神們的作品;編譯完成後模擬器上會WebDriverAgent的圖示,點選圖示會啟動然後結束,很像閃退的效果

 

之前推薦大家安裝desktop版本的原因就是desktop版本可以擷取到App UI的結構,配置好即可啟動,並讀取App的UI結構,點選紅框部分進入到UI框架讀取介面

 

配置好需要的desired capabilities,點選啟動即可,其中每一項的內容分析會在下一部分一一解析

{

"bundleId": "com.latsen.Pawfit",

"deviceName": "Simulator",

"platformVersion": "14.2",

"platformName": "iOS",

"automationName": "Appium",

"noReset": true,

"udid": "F1B7F19F-C517-4587-8DE5-8EE73B8BBFDF"

}

啟動看看結果吧

好的,第一個階段算是完成了,環境搭建是我覺得這一個開發iOS測試指令碼的最難部分,安裝期間會遇到各種各樣的問題,可以百度仰慕下大神的作品及其解決方案,祝您順利

這個準備也是需要費點時間的工作,我這裡三言兩語就說完,但不代表這個步驟很簡單,出現的意外可能就會讓您前面的努力白費;

準備好您的模擬器或者手機,這裡建議使用模擬器測試,手機會因為SDK的問題導致無法連線到手機,啟動模擬器;

接下來就是準備好需要測試的APP檔案,這個地方需要注意下,如果該APP檔案不適配您的電腦,

啟動APP請求網路是時APP會閃退,從而無法進行開發,我自然就遇到過,奇怪的就是網上為何都沒出現過呢???為此,我花了三瓶可樂來招待開發小哥哥?;檢查下APP是否可以正常在中執行

文件準備,官方的穩定簡直就是爛透了,這裡建議使用看雲的文件,可能需要登入

https://www.kancloud.cn/testerhome/appium_docs_cn/2001597

Appium配置文件

https://appium.io/docs/cn/writing-running-appium/caps/

完成了以上的折磨後,現在可以開啟美麗的開發過程了,啟動你的編譯器吧

pip安裝appium的python客戶端程式碼

pip install Appium-Python-Client

from appium import webdriver
my_ios_driver=None
import json
class my_driver(object):
    session=None
    def __init__(self):
        self.session=self.get_my_driver(self.get_deried_cap())
    def get_deried_cap(self):
        caps = {}
        #APP檔案在電腦中的位置,當模擬器中安裝後,可以去掉該配置
        # caps["app"] = "/Users/kefeng/Desktop/Pawfit1230_1.zip"
        #將測試APP的包名
        caps["bundleId"] = "com.latsen.Pawfit"
        #模擬器名稱
        caps["deviceName"] = "Simulator"
        #模擬器系統版本
        caps["platformVersion"] = "14.2"
        #測試的平臺
        caps["platformName"] = "iOS"
        #測試期間使用的驅動
        caps["automationName"] = "XCUITest"
        #是否需要重置APP,不需要寫False
        caps["noReset"] = True
        #命令執行的超時時間,這個很重要
        caps["newCommandTimeout"]=600
        #是否中斷回話,並重啟app
        caps['autoLaunch']=False
        #裝置的UDID
        caps["udid"] = "F1B7F19F-C517-4587-8DE5-8EE73B8BBFDF"
        return caps
    def get_my_driver(self,caps):
        driver=webdriver.Remote("http://localhost:4723/wd/hub", caps)
        driver.set_page_load_timeout(50000)
        driver.set_script_timeout(50000)
        driver.implicitly_wait(10)
        # 設定許可權,可根據xcrun simctl privacy命令檢視
        return driver
    def disconnect(self):
        my_ios_driver.quit()
my_ios_driver=my_driver().session
if __name__ == '__main__':
    my_ios_driver.execute_script('mobile: setPermission','io.appium.example',{ 'location-always': 'yes'})

以上註釋中提到的幾個配置項都非常非常重要,如果配置錯誤,將無法啟動;

#是否中斷回話,並重啟app caps["newCommandTimeout"]=600 #是否中斷回話,並重啟app caps['autoLaunch']=False

這兩項在測試和開發中相當重要,尤其是caps['autoLaunch']=False,他可以幫助你不用每次重啟APP即可進行測試,這在開發中極其的方便,我們並不是每次都想重啟APP來進行測試,尤其是後期再testsuit套件測試時;

開發過程中,經常會出現的錯誤就是元素查詢異常,Appium提供了多樣的元素獲取方式,如下:

根據類名:

#根據類名:
pet_breed=my_ios_driver.find_element_by_class_name('XCUIElementTypeTextField')[
pet_breed=my_ios_driver.find_elements_by_class_name('XCUIElementTypeTextField')[2]

#根據xpath
my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Dog"]').click()
my_ios_driver.find_elements_by_xpath('//XCUIElementTypeButton[@name="Dog"]')[0].click()

#根據predicate
my_ios_driver.find_element_by_ios_predicate('label == "Female"').click()

 

當然還會有其他的,可以根據您的需求來選擇,例如我常用的程式碼截選

當然還會有其他的,可以根據您的需求來選擇,例如我常用的程式碼截選
if type == "SUBSCRIBABLE" and status == "INACTIVE":
    # paypal方式使用手動測試
    pay_method = "1"

    if str(pay_method) == '1':
        device_id.clear()
        device_id.send_keys(barcode)
        self.hide_key_board()
        bind_buttom.click()
        sleep(5)
        # 啟動支付流程
        # 檢查優惠碼有效性
        code=my_ios_driver.find_element_by_class_name('XCUIElementTypeTextField')
        # 檢查無效
        code.clear()
        code.send_keys("@@@@@@@")
        self.hide_key_board()
        my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Buy now"]').click()
        sleep(5)
        # 檢查過期優惠碼
        code.clear()
        code.send_keys("asdfgv")
        self.hide_key_board()
        my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Buy now"]').click()
        sleep(5)
        # 已經被使用
        code.clear()
        code.send_keys("qawsde")
        self.hide_key_board()
        my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Buy now"]').click()
        # TODO 優惠碼支付,請完善
        my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="OK"]').click()
        #返回繫結介面,等待繫結完成
        sleep(10)
        my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="OK"]').click()
    else:
        #TODO 由於paypal的原因,支付完成後可能會出現pending狀態而無法繼續接著其他步驟,可能需要單獨處理
        device_id.clear()
        device_id.send_keys(barcode)
        self.hide_key_board()
        bind_buttom.click()
        sleep(5)
        buy_now=my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Buy now"]')
        buy_now.click()
        sleep(5)
        my_ios_driver.find_elements_by_class_name('XCUIElementTypeButton')[2].click()
        sleep(5)
        if self.utis.exists('xpath','//XCUIElementTypeButton[@name="Next"]',30):
            my_ios_driver.find_element_by_class_name('XCUIElementTypeTextField').send_keys("accounting-buyer@latsen.com")
            my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Next"]').click()
            sleep(10)
            my_ios_driver.find_element_by_class_name('XCUIElementTypeSecureTextField').send_keys("paypalDev123")
            my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Log in"]').click()
        else:
            my_ios_driver.find_element_by_class_name('XCUIElementTypeTextField').send_keys("accounting-buyer@latsen.com")
            my_ios_driver.find_element_by_class_name('XCUIElementTypeSecureTextField').send_keys("paypalDev123")
            self.hide_key_board()
            my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Log in"]').click()
        sleep(15)
        my_ios_driver.execute_script("mobile:swipe", {"direction": "up", 'element': my_ios_driver.find_element_by_xpath('  //XCUIElementTypeStaticText[@name="Pay with"]'), "duration": 1})
        my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Agree and Subscribe"]').click()

else:
    device_id.clear()
    device_id.send_keys(barcode)
    self.hide_key_board()
    bind_buttom.click()
    # 優惠碼購買完成,但伺服器裝置狀態未進行更改
    #處理不同型號機器彈框不同問題
    # sleep(10)
    # my_ios_driver.set_page_load_timeout(10000)
    sleep(15)
    if type=="SUBSCRIBABLE":
        my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="OK"]').click()
    #P
    else:
        my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Yes"]').click()

用到sleep簡直就是噩夢,但不得不用,官方的等待方式經常會導致元素查詢異常,哪怕我已經做了異常處理,場景多變,況且我還是菜;

# TODO 修改程式碼,返回一個元組,裡面裡面存放元素是否存在和控制元件物件

# TODO 修改程式碼,返回一個元組,裡面裡面存放元素是否存在和控制元件物件
def exists(self, what_method, resource_text, timeout=1):
    i = 0
    flag = False
    driver = my_ios_driver
    while timeout > i:
        try:
            if what_method == "accid":
                driver.find_element_by_accessibility_id(resource_text)
            elif what_method == "class":
                driver.find_element_by_class_name(resource_text)
            elif what_method == "css":
                driver.find_element_by_class_name(resource_text)
            elif what_method == "xpath":
                driver.find_element_by_xpath(resource_text)
            elif what_method == "pred":
                driver.find_element_by_ios_predicate(resource_text)
            elif what_method == "tag":
                driver.find_element_by_tag_name(resource_text)
            elif what_method == "name":
                driver.find_element_by_name(resource_text)
            elif what_method == "id":
                driver.find_element_by_id(resource_text)
            flag = True
            return flag
        except Exception as e:
            print(e)
            i += 1
            continue
    return flag

更多的程式碼細節不能太多暴露,請原諒

配合unittest使用,設計合理的測試流程,即可完成整個APP的全部用例的測試;

from ios.ios_app_edit_user_info_testcase import test_edit_user_profile
from ios.ios_app_register_testcase import Register_TestCase
from ios.ios_app_edit_pet_info_testcase import Edit_Pet_Info

if __name__ == '__main__':
    testcase = unittest.TestLoader().loadTestsFromTestCase(Login_Test)
    test_edit_user_profile = unittest.TestLoader().loadTestsFromTestCase(test_edit_user_profile)
    FindPassword = unittest.TestLoader().loadTestsFromTestCase(FindPassword)
    Register_TestCase = unittest.TestLoader().loadTestsFromTestCase(Register_TestCase)
    add_pet=unittest.TestLoader().loadTestsFromTestCase(AddPet_TestCase)
    edit_pet=unittest.TestLoader().loadTestsFromTestCase(Edit_Pet_Info)
    suite = unittest.TestSuite([testcase,add_pet,edit_pet,test_edit_user_profile,FindPassword,Register_TestCase])
    report='/Users/kefeng/Documents/TestCases/reports/testcase_report.html'
    with(open(report, 'wb')) as fp:
        runner = HTMLTestRunner(
            stream=fp,
            title='<project name> iOS 測試報告',
            description='describe: iOS 測試報告'
        )
        runner.run(suite)

原先想著可以錄屏給親們看個效果,但是發現模擬器似乎沒有錄屏功能,如果下次拿到錄屏,一定給補上?

一下截圖是測試結束後的報告,全部59個testcase可以在17分鐘完成測試,下次可以喝著肥宅水,看著自動化指令碼執行即可,當然目前程式碼的健壯性需要加強,大家一起努力吧

目錄

6:告一段落


祝願和我一樣的菜鳥能快速成長,開心每一天。

 

相關文章