相對於Android來說,iOS比較封閉。這一點,在設計和評估自動化測試方案的時候感覺尤其強烈。iOS平台上沒有特別好用的自動化測試工具。蘋果針對iOS提供了UI Automation的Instruments工具,以及相配合使用的Javascript庫,但是使用起來有很大的局限性。主要問題是必須使用Javascript來編寫測試腳本,不支持其他語言,很難實現復雜的功能。而且,在一台mac機上同時只能運行一個Instruments實例,無法對多個設備同時進行測試。在多數游戲應用中,UI都不是使用標准控件的,所以不可避免的要使用圖像識別技術。而iOS UI Automation API里面除了截屏的功能並沒有提供多少幫助。
所幸的是我們找到了UIAHost.performTaskWithPathArgumentsTimeout()方法。這個方法是用來調用外部程序的。巧妙地利用這個方法可以實現比較復雜的功能。但是我仍然希望測試邏輯能用Python來寫,因為Python用起來相當順手而且有成熟的測試框架。
要讓UI Automation的Javascript腳本聽從Python腳本的指揮,可以把Javascript腳本寫成一個服務器,接受來自Python腳本的指令,並調用相應的API完成任務。通信的任務可以使用socket。當然Javascript腳本本身無法完成這個任務,所以需要調用外部程序來實現。這個外部程序可以用Python來寫,我稱之為slave.py,而Javascript腳本就是master.js,因為是master創建的slave進程。當然實際上slave並不聽命於master,master反而要聽從slave從socket獲得的指令。
這樣一來,只需要寫個驅動層,把API調用包裝一下,通過socket傳輸到slave.py,再通過slave.py的stdout返回到master.js,再通過調用UI Automation API就實現了Python腳本的自動化測試。當然本文沒有涉及很多細節實現問題,留給以后有時間再闡述。
以下是簡化的master.js示例代碼:
UIALogger.logMessage("Instruments started.") var target = UIATarget.localTarget(); var app = target.frontMostApp(); var window = app.mainWindow(); var host = target.host(); var screenshotPath = "screen"; var python_path = host.performTaskWithPathArgumentsTimeout("/usr/bin/which", ['python'], 1).stdout.replace("\n", ""); if (python_path == "") { UIALogger.logError("python is not found."); } else { while(1) { var result = host.performTaskWithPathArgumentsTimeout( python_path,['InstrumentsSlave.py'], 30); var ins = ("" + result.stdout).split('\n'); if (ins[0] == 'exitApp') break; switch (ins[0]) { case 'tap': var x = ins[1]; var y = ins[2]; target.tap({x:x, y:y}) break; case 'input': var s = ins[1]; app.keyboard().typeString(s) break; case 'captureScreen': target.captureScreenWithName(screenshotPath); break; default: break; } } }