Appium的实现理解
Appium的实现理解Appium的架构起步下载appium的源码,并安装依赖:git clone https://github.com/appium/appium.git
npm install启动appium:node .这个启动命令实际是执行的:node build\main.js(package.json中指定了main入口):...
"main": "./build/lib/main.js",
"bin": {
"appium": "./build/lib/main.js"
},
.../build/main.js是由/lib/main.js经babel翻译后的结果,所以,我们来看下/lib/main.js来理解appium的流程。
(备注:由于appium源码执行都是执行的编译后的方法,即build目录下,因此如果你想要调试进行测试,需要在各个模块build目录下更改调试,如果更改源码,需要gulp transpile进行编译)appium server
appium server端实现了HTTP REST API接口,将client端发来的API请求,解析,发送给执行端。apium server,以及其他的driver(android,ios)都实现了basedriver类。basedriver定义了session的创建,命令的执行方式(cmd执行)。appium server(appium driver)大致的流程为:
[*]解析命令行参数
[*]注册路由方法
[*]解析路由
我们看一下appium server的源码实现。import { server as baseServer } from 'appium-base-driver';
import getAppiumRouter from './appium';
...
async function main (args = null) {
//解析参数
let parser = getParser();
let throwInsteadOfExit = false;
if (args) {
args = Object.assign({}, getDefaultArgs(), args);
if (args.throwInsteadOfExit) {
throwInsteadOfExit = true;
delete args.throwInsteadOfExit;
}
} else {
args = parser.parseArgs();
}
await logsinkInit(args);
await preflightChecks(parser, args, throwInsteadOfExit);
//输出欢迎信息
await logStartupInfo(parser, args);
//注册接口路由,参见(appium-base-driver\lib\jsonwp\Mjsonwp.js)
let router = getAppiumRouter(args);
//express server类(appium-base-driver\lib\express\server.js)
//将注册的路由,传递给express注册.
let server = await baseServer(router, args.port, args.address);
try {
//是否为appium grid的node节点
if (args.nodeconfig !== null) {
await registerNode(args.nodeconfig, args.address, args.port);
}
} catch (err) {
await server.close();
throw err;
}
process.once('SIGINT', async function () {
logger.info(`Received SIGINT - shutting down`);
await server.close();
});
process.once('SIGTERM', async function () {
logger.info(`Received SIGTERM - shutting down`);
await server.close();
});
logServerPort(args.address, args.port);
return server;
}
...
//路由
//appium.js,下面会讲解路由解析
function getAppiumRouter (args) {
let appium = new AppiumDriver(args);
return routeConfiguringFunction(appium);
}
URL路由解析上面说道,路由注册。所有支持的请求都METHOD_MAP这个全局变量里面。它是一个path:commd的对象集合。路由执行过程是:
[*]检查命令类型(是否包含session)
[*]设置代理
[*]检查命令类型与参数
[*]执行命令
我们来详细看一下(routeConfiguringFunction)到底做了什么(appium-base-driver\lib\mjsonwp\Mjsonwp.js):
function routeConfiguringFunction (driver) {
if (!driver.sessionExists) {
throw new Error('Drivers used with MJSONWP must implement `sessionExists`');
}
if (!(driver.executeCommand || driver.execute)) {
throw new Error('Drivers used with MJSONWP must implement `executeCommand` or `execute`');
}
// return a function which will add all the routes to the driver
return function (app) {
//(#route_config),是所有的路由配置,key为path,value为method的数组
//对METHOD_MAP的配置进行绑定
for (let of _.toPairs(METHOD_MAP)) {
for (let of _.toPairs(methods)) {
// set up the express route handler
buildHandler(app, method, path, spec, driver, isSessionCommand(spec.command));
}
}
};
}
//路由绑定
//示例:
/*
'/wd/hub/session': {
POST: {command: 'createSession', payloadParams: {required: ['desiredCapabilities'], optional: ['requiredCapabilities', 'capabilities']}}
},
即:
method: POST
path: /wd/hub/session
spec: array
driver: appium
*/
function buildHandler (app, method, path, spec, driver, isSessCmd) {
let asyncHandler = async (req, res) => {
let jsonObj = req.body;
let httpResBody = {};
let httpStatus = 200;
let newSessionId;
try {
//判断是否是创建session命令(包含createSession,getStatus,getSessions)
//是否有session
if (isSessCmd && !driver.sessionExists(req.params.sessionId)) {
throw new errors.NoSuchDriverError();
}
//设置了代理则透传
if (isSessCmd && driverShouldDoJwpProxy(driver, req, spec.command)) {
await doJwpProxy(driver, req, res);
return;
}
//命令是否支持
if (!spec.command) {
throw new errors.NotImplementedError();
}
//POST参数检查
if (spec.payloadParams && spec.payloadParams.wrap) {
jsonObj = wrapParams(spec.payloadParams, jsonObj);
}
if (spec.payloadParams && spec.payloadParams.unwrap) {
jsonObj = unwrapParams(spec.payloadParams, jsonObj);
}
checkParams(spec.payloadParams, jsonObj);
//构造参数
let args = makeArgs(req.params, jsonObj, spec.payloadParams || []);
let driverRes;
if (validators) {
validators(...args);
}
//!!!!执行命令
//捕获返回值
if (driver.executeCommand) {
driverRes = await driver.executeCommand(spec.command, ...args);
} else {
driverRes = await driver.execute(spec.command, ...args);
}
// unpack createSession response
if (spec.command === 'createSession') {
newSessionId = driverRes;
driverRes = driverRes;
}
...
} catch (err) {
= getResponseForJsonwpError(actualErr);
}
if (_.isString(httpResBody)) {
res.status(httpStatus).send(httpResBody);
} else {
if (newSessionId) {
httpResBody.sessionId = newSessionId;
} else {
httpResBody.sessionId = req.params.sessionId || null;
}
res.status(httpStatus).json(httpResBody);
}
};
// add the method to the app
app(path, (req, res) => {
B.resolve(asyncHandler(req, res)).done();
});
}创建session 与 executeCommandlib\appium.js上面说了appium server已经启动了,第一件事情,当然是创建session,然后把command交给这个session的不同driver去执行了。
appium先根据caps进行session创建(getDriverForCaps),然后保存InnerDriver到当前session,以后每次执行命令(executeDCommand)会判断是否为appiumdriver的命令,不是则转给相应的driver去执行命令(android,ios等)。async createSession (caps, reqCaps) {
caps = _.defaults(_.clone(caps), this.args.defaultCapabilities);
let InnerDriver = this.getDriverForCaps(caps);
this.printNewSessionAnnouncement(InnerDriver, caps);
if (this.args.sessionOverride && !!this.sessions && _.keys(this.sessions).length > 0) {
for (let id of _.keys(this.sessions)) {
log.info(` Deleting session '${id}'`);
try {
await this.deleteSession(id);
} catch (ign) {
}
}
}
let curSessions;
try {
curSessions = this.curSessionDataForDriver(InnerDriver);
} catch (e) {
throw new errors.SessionNotCreatedError(e.message);
}
let d = new InnerDriver(this.args);
let = await d.createSession(caps, reqCaps, curSessions);
this.sessions = d;
this.attachUnexpectedShutdownHandler(d, innerSessionId);
d.startNewCommandTimeout();
return ;
}
async executeCommand (cmd, ...args) {
if (isAppiumDriverCommand(cmd)) {
return super.executeCommand(cmd, ...args);
}
let sessionId = args;
return this.sessions.executeCommand(cmd, ...args);
}
在basedriver中executeDCommand其实是调用类的cmd定义的方法。
页:
[1]