相對於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;
}
}
}
