首先,大家都知道 appium 在 android 平台上的底层实现使用的是 UIAutomator 。而 UIAutomator 是具有应用无关的特性的,即不需要打开应用即可控制系统界面。然而 appium 默认 把启动应用与执行测试绑定在了一起,因此无法使用像 UIAutomator 那样应用无关的方式执行用例。
接下来,我会简单介绍一下 appium 在 android 平台初始化 session 的主要步骤,并告诉大家如何在修改数行代码后实现不启动应用直接执行用例。
考虑到这部分不是最重点的部分,因此用文字说明,方便大家理解。
此处的初始化 session 指脚本中 webdriver.Remote('http://localhost:4723/wd/hub,desired_caps')
这个初始化 driver 的语句。
Trying to run a session for device 'android' but that device hasn't been configured. Run config
错误。从上面的步骤可以看到,和 uiautomator 相关的操作只有:
而安装/启动被测应用则是不同的操作。两者耦合度很低,意味着我们可以通过去掉安装/启动的步骤来实现不启动应用完成 session 初始化。
首先,上面提到的配置 android 平台的 session 的主要过程都在这个函数中:
lib/devices/android/android.js
... Android.prototype.start = function (cb, onDie) { this.launchCb = cb; this.uiautomatorExitCb = onDie; logger.info("Starting android appium"); async.series([ this.initJavaVersion.bind(this), this.initAdb.bind(this), this.packageAndLaunchActivityFromManifest.bind(this), this.initUiautomator.bind(this), this.prepareDevice.bind(this), this.checkApiLevel.bind(this), this.pushStrings.bind(this), this.processFromManifest.bind(this), this.uninstallApp.bind(this), this.installAppForTest.bind(this), this.forwardPort.bind(this), this.pushAppium.bind(this), this.initUnicode.bind(this), this.pushSettingsApp.bind(this), this.pushUnlock.bind(this), function (cb) {this.uiautomator.start(cb);}.bind(this), this.wakeUp.bind(this), this.unlock.bind(this), this.getDataDir.bind(this), this.setupCompressedLayoutHierarchy.bind(this), this.startAppUnderTest.bind(this), this.initAutoWebview.bind(this), this.setActualCapabilities.bind(this) ], function (err) { if (err) { this.shutdown(function () { this.launchCb(err); }.bind(this)); } else { this.didLaunch = true; this.launchCb(null, this.proxySessionId); } }.bind(this)); };
非常一目了然。注释掉和应用安装/启动相关的函数后,它会变成这个样子:
Android.prototype.start = function (cb, onDie) {
this.launchCb = cb;
this.uiautomatorExitCb = onDie;
logger.info("Starting android appium");
async.series([
this.initJavaVersion.bind(this),
this.initAdb.bind(this),
//this.packageAndLaunchActivityFromManifest.bind(this),
this.initUiautomator.bind(this),
this.prepareDevice.bind(this),
this.checkApiLevel.bind(this),
//this.pushStrings.bind(this),
//this.processFromManifest.bind(this),
//this.uninstallApp.bind(this),
//this.installAppForTest.bind(this),
this.forwardPort.bind(this),
this.pushAppium.bind(this),
this.initUnicode.bind(this),
this.pushSettingsApp.bind(this),
this.pushUnlock.bind(this),
function (cb) {this.uiautomator.start(cb);}.bind(this),
this.wakeUp.bind(this),
this.unlock.bind(this),
this.getDataDir.bind(this),
this.setupCompressedLayoutHierarchy.bind(this),
//this.startAppUnderTest.bind(this),
//this.initAutoWebview.bind(this),
this.setActualCapabilities.bind(this)
], function (err) {
if (err) {
this.shutdown(function () {
this.launchCb(err);
}.bind(this));
} else {
this.didLaunch = true;
this.launchCb(null, this.proxySessionId);
}
}.bind(this));
};
这个时候所有启动应用的相关操作都不会进行。 session 建立后我们仍然能通过 appium 测试脚本控制手机界面,绝大部分操作仍然能正常进行。
# -*- coding:utf-8 -*-
import os
from appium import webdriver
# Returns abs path relative to this file and not cwd
PATH = lambda p: os.path.abspath(
os.path.join(os.path.dirname(__file__), p)
)
if __name__ == "__main__":
desired_caps = {}
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '4.4'
desired_caps['deviceName'] = 'e4d42545'
desired_caps['unicodeKeyboard'] = 'true'
desired_caps['resetKeyboard'] = 'true'
desired_caps['app'] = PATH(
'../app/Dianping_dianping-web_7.1.1.apk'
)
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
print "Start driver success!"
print "Trying to click browser icon"
driver.find_element_by_xpath('//android.widget.TextView[@text="Browser"]').click()
print "Finish clicking browser icon. The browser should be opened."
driver.quit()