天天看點

手機自動化測試:Appium源碼分析之跟蹤代碼分析六

手機自動化測試:Appium源碼分析之跟蹤代碼分析六

加載子產品

var logger = require('./logger.js').get('appium')

  , status = require('./status.js')

  , _ = require('underscore')

  , safely = require('./helpers').safely;

加載了4個子產品,3個本地子產品(logger,status,helpers)和一個核心工具子產品(underscore)。本地子產品我們後續再講解,這個工具子產品是做什麼用的,我找到一篇文章講解這個underscore,從這篇文章可以了解到這個子產品主要提供了一些常用的函數,大概80個。

7個函數

reponses子產品中包含7個函數,下面我們一步一步解釋

getSessionId函數

//得到session的id号,每一個session都有一個獨特的id用來辨別

var getSessionId = function (req, response) {

  //判斷傳遞過來的參數response是否初始化,如果已經初始化,sessionId的值就指派為response的屬性sessionId的值

  //否則指派為undefined

  var sessionId = (typeof response === 'undefined') ? undefined : response.sessionId;

  //經過上面的指派後,如果sessionId的值為undefined,那麼就要進一步處理

  if (typeof sessionId === "undefined") {

    if (req.appium) {

      //如果req.appium初始化過,就将appium中的屬性sessionId指派給目前變量sessionId

      //需要注意的是後面的||null寫法,如果req.appium.sessionId是沒有定義的,那麼會走到||後面,意思也是

      //給sessionId定義為null,如果不加||null,會發生什麼呢?(sessionId=undefined)...

      sessionId = req.appium.sessionId || null;

    } else {

      //如果req.appium沒有初始化,那麼就直接将sessionId置為空

      sessionId = null;

    }

  }

  //因為我們要求傳回的sessionId是一個字元串,那麼我就要判斷該類型是否正确,如果不正确,也需要置為null值

  if (typeof sessionId !== "string" && sessionId !== null) {

    sessionId = null;

  return sessionId;

};

notImplementedInThisContext

//當我們調用的方法,并不存在的時候,會調用該方法,該方法一般是在裝置端,執行查找或對某一個控件進行操作的。

var notImplementedInThisContext = function (req, res) {

  //首先列印提示資訊

  logger.debug("Responding to client that a method is not implemented " +

              "in this context");

  safely(req, function () {

    //狀态設定為501,并傳回一個json字元串

    res.status(501).send({

      status: status.codes.UnknownError.code

    , sessionId: getSessionId(req)

    , value: {

      //提示資訊消息體

        message: "Not implemented in this context, try switching " +

                 "into or out of a web view"

      }

    });

  });

上面的函數體内有2個地方需要了解:safely()和res.status(501).send()

函數 意義
safely helpers.js子產品中safely方法
res.status(501).send 将response狀态設定為501,然後發送一個消息

下面是safely方法的源碼,也很簡單,就是調用了嵌套的回調函數。

// Mainly used to wrap http response methods, or for cases where errors

// perdure the domain

var safely = function () {

  var args = new (Args)(arguments);

  var req = args.all[0];

  var fn = args.callback;

  try {

    fn();

  } catch (err) {

    logger.error('Unexpected error:', err.stack, getRequestContext(req));

respondError

//傳回錯誤資訊

var respondError = exports.respondError = function (req, res, statusObj, value) {

  //設定錯誤資訊的内容和狀态碼,因為message要求是字元串類型,code是×××類型

  var code = 1, message = "An unknown error occurred";

  var newValue = value;

  if (typeof statusObj === "string") {

    message = statusObj;

  } else if (typeof statusObj === "undefined") {

    message = "undefined status object";

  } else if (typeof statusObj === "number") {

    code = statusObj;

    message = status.getSummaryByCode(code);

  } else if (typeof statusObj.code !== "undefined") {

    code = statusObj.code;

    message = statusObj.summary;

  } else if (typeof statusObj.message !== "undefined") {

    message = statusObj.message;

  //如果value的值是一個對象類型的值,需要從這個對象中解析出message屬性

  //然後進行拼接組成一個json字元串,指派給newValue,如果不是一個對象類型的值

  //直接組裝成json字元串

  if (typeof newValue === "object") {

    if (newValue !== null && _.has(value, "message")) {

      // make sure this doesn't get obliterated

      value.origValue = value.message;

      message += " (Original error: " + value.message + ")";

    newValue = _.extend({message: message}, value);

  } else {

    newValue = {message: message, origValue: value};

  //拼接傳回資訊json字元串對象reponse

  var response = {status: code, value: newValue};

  //擷取sessionId

  response.sessionId = getSessionId(req, response);

  logger.debug("Responding to client with error: " + JSON.stringify(response));

  //調用helpers.js子產品的safely函數,就是調用裡面的嵌套函數

    res.status(500).send(response);

respondSuccess

var respondSuccess = exports.respondSuccess = function (req, res, value, sid) {

  var response = {status: status.codes.Success.code, value: value};

  response.sessionId = getSessionId(req, response) || sid;

  if (typeof response.value === "undefined") {

    response.value = '';

  var printResponse = _.clone(response);

  var maxLen = 1000;

  if (printResponse.value !== null &&

      typeof printResponse.value.length !== "undefined" &&

      printResponse.value.length > maxLen) {

    printResponse.value = printResponse.value.slice(0, maxLen) + "...";

  res.jsonResp = JSON.stringify(printResponse);

  logger.debug("Responding to client with success: " + res.jsonResp);

    res.status(200).send(response);

這個函數我就不解釋了,因為大緻的方式都是一樣的,都是先解析出列印的消息體,然後發送出去,隻是這裡面的response狀态是200,因為是成功的。該方法調用的log資訊,我們會經常見到。

getResponseHandler

exports.getResponseHandler = function (req, res) {

  //直接傳回匿名函數

  return function (err, response) {

    //先判斷response是否已經初始化,或者是否初始化為空

    if (typeof response === "undefined" || response === null) {

      //設定為空的json字元串

      response = {};

    //判斷錯誤的資訊,符合如下條件的,直接抛出異常

    if (err !== null && typeof err !== "undefined" && typeof err.status !== 'undefined' && typeof err.value !== 'undefined') {

      throw new Error("Looks like you passed in a response object as the " +

                      "first param to getResponseHandler. Err is always the " +

                      "first param! Fix your codes!");

    } else if (err !== null && typeof err !== "undefined") {

      if (typeof err.name !== 'undefined') {

        if (err.name === 'NotImplementedError') {

          //如果錯誤的name值為NotImplementedError,就調用notImplementedInThisContext函數

          notImplementedInThisContext(req, res);

        } else if (err.name === "NotYetImplementedError") {

          //如果錯誤的name值為NotYetImplementedError,就調用notYetImplemented函數

          notYetImplemented(req, res);

        } else {

          //其他類型都調用respondError

          respondError(req, res, status.codes.UnknownError.code, err);

        }

      } else {

        var value = response.value;

        if (typeof value === "undefined") {

          value = '';

        //如果錯誤類型name屬性值為undefined,我們調用respondError的時候傳入的參數為value值

        respondError(req, res, err.message, value);

      //如果錯誤資訊為空或者未定義,我們需要根據狀态碼的不同,調用不同函數處理

      if (response.status === 0) {

        respondSuccess(req, res, response.value, response.sessionId);

        respondError(req, res, response.status, response.value);

  };

終于到了我們真正要解釋的函數了-getResponseHandler,從我上面的解釋可以看出來,該函數是一個控制類的函數,所有的調用經過該函數處理後,會根據傳入參數的不同,調用不同的函數,是以這個函數名字取為handler,以為回複處理器。

checkMissingParams

exports.checkMissingParams = function (req, res, params, strict) {

  //如果strict未定義,設定為false,說明strict為boolean類型的值

  if (typeof strict === "undefined") {

    strict = false;

  var missingParamNames = [];

  //疊代params所有的元素,擷取所有沒有定義的或者值與strict相反的參數,添加到missingParamNames數組中

  _.each(params, function (param, paramName) {

    if (typeof param === "undefined" || (strict && !param)) {

      missingParamNames.push(paramName);

  if (missingParamNames.length > 0) {

    //将數組轉化為json字元串

    var missingList = JSON.stringify(missingParamNames);

    logger.debug("Missing params for request: " + missingList);

    //response的狀态為400,消息體為上面的json字元串的

    safely(req, function () {

      res.status(400).send("Missing parameters: " + missingList);

    return false;

    return true;

上面的函數是找到為定義的參數或者為null的參數

notYetImplemented

//在getResponseHandler函數中我們看到當err.name為NotYetImplementedError時,會調用該函數

var notYetImplemented = exports.notYetImplemented = function (req, res) {

  //與notImplementedInThisContext類似,都是将response的狀态設定為501,然後消息體不同

  logger.debug("Responding to client that a method is not implemented");

        message: "Not yet implemented. " +

               "Please help us: http://appium.io/get-involved.html"