appium get_attribute 方法

1.相關鏈接:http://blog.csdn.net/bear_w/article/details/50330753

2.https://testerhome.com/topics/2606(原文,重點看)

Appium 將在下個版本(已經)加入 get_attribute('contentDescription') 方法來獲取 content-desc 屬性:
https://github.com/appium/appium/pull/5189

問題描述

當使用類似下面的代碼獲取元素的 content-desc 屬性時,會報 NoSuchElement 錯誤:

# python
self.driver.find_element_by_id("id").get_attribute("content-desc")

但使用如下代碼卻能正常執行:

# python
self.driver.find_element_by_id("id").click()

很明顯,這個錯誤原因不是找不到元素,而是 get_attribute 出問題。

問題原因探究

appium server 在 Android 原生應用上獲取 attribute 的大致流程爲:

  1. 從 client 收到獲取 attribute 的請求
  2. 把請求轉發給在手機上運行的 bootstrap
  3. bootstrap 調用相關方法進行實際操作
  4. bootstrap 返回結果給 appium server
  5. appium server 把結果返回給 client

通過排查各部分的代碼發現,錯誤是在 bootstrap 產生的(排查過程涉及代碼有點多,所以這裏就不解釋了),所以看看 bootstrap 相關源碼:

lib/devices/android/bootstrap/src/io/appium/android/bootstrap/handler/GetAttribute.Java

@Override
  public AndroidCommandResult execute(final AndroidCommand command)
      throws JSONException {
    if (command.isElementCommand()) {
      // only makes sense on an element
      final Hashtable<String, Object> params = command.params();

      try {
        // 獲取需要操作的 element 實例
        final AndroidElement el = command.getElement();
        // 獲取需要獲取的 attribute 的名稱
        final String attr = params.get("attribute").toString();
        if (attr.equals("name") || attr.equals("text")
            || attr.equals("className") || attr.equals("resourceId")) {
          // 如果 attribute 名稱爲 name, text, className, resourceId ,調用元素的 getStringAttribute 方法獲取
          return getSuccessResult(el.getStringAttribute(attr));
        } else {
          // 否則調用 getBoolAttribute 獲取 (我們的 'content-desc' 執行的是這段語句)
          return getSuccessResult(String.valueOf(el.getBoolAttribute(attr)));
        }
      } catch (final NoAttributeFoundException e) {
        // 從這裏開始就是第一個坑,無論是什麼錯誤,最終返回的都是 NO_SUCH_ELEMENT
        return new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT,
            e.getMessage());
      } catch (final UiObjectNotFoundException e) {
        return new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT,
            e.getMessage());
      } catch (final Exception e) { // el is null
        return new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT,
            e.getMessage());
      }
    } else {
      // 沒有給出需要獲取屬性的 element ,返回錯誤信息
      return getErrorResult("Unable to get attribute without an element.");
    }
  }

其中 getStringAttribute 和 getBoolAttribute 源碼如下:

lib/devices/android/bootstrap/src/io/appium/android/bootstrap/AndroidElement.java

public String getStringAttribute(final String attr)
      throws UiObjectNotFoundException, NoAttributeFoundException {
    String res;
    if (attr.equals("name")) {
      // 坑2:屬性名稱爲 name 時,會先嚐試獲取 content-desc ,如果 content-desc 爲空,則獲取 text 。說白了,就算用 name 也不能保證你獲取的就是 content-desc
      res = getContentDesc();
      if (res.equals("")) {
        res = getText();
      }
    } else if (attr.equals("text")) {
      res = getText();
    } else if (attr.equals("className")) {
      res = getClassName();
    } else if (attr.equals("resourceId")) {
      res = getResourceId();
    } else {
      throw new NoAttributeFoundException(attr);
    }
    return res;
  }
...
public boolean getBoolAttribute(final String attr)
      throws UiObjectNotFoundException, NoAttributeFoundException {
    boolean res;
    // 這個方法只會獲取值爲布爾類型的屬性,我們的 content-desc 的值不是布爾類型的,所以拋出 NoAttributeFoundException
    if (attr.equals("enabled")) {
      res = el.isEnabled();
    } else if (attr.equals("checkable")) {
      res = el.isCheckable();
    } else if (attr.equals("checked")) {
      res = el.isChecked();
    } else if (attr.equals("clickable")) {
      res = el.isClickable();
    } else if (attr.equals("focusable")) {
      res = el.isFocusable();
    } else if (attr.equals("focused")) {
      res = el.isFocused();
    } else if (attr.equals("longClickable")) {
      res = el.isLongClickable();
    } else if (attr.equals("scrollable")) {
      res = el.isScrollable();
    } else if (attr.equals("selected")) {
      res = el.isSelected();
    } else if (attr.equals("displayed")) {
      res = el.exists();
    } else {
      throw new NoAttributeFoundException(attr);
    }
    return res;
  }

總結

1、獲取 content-desc 的方法爲 get_attribute("name") ,而且還不能保證返回的一定是 content-desc (content-desc 爲空時會返回 text 屬性值)
2、get_attribute 方法不是我們在 uiautomatorviewer 看到的所有屬性都能獲取的(此處的名稱均爲使用 get_attribute 時使用的屬性名稱):

可獲取的:

字符串類型:

  • name(返回 content-desc 或 text)
  • text(返回 text)
  • className(返回 class,只有 API=>18 才能支持)
  • resourceId(返回 resource-id,只有 API=>18 才能支持)

布爾類型(如果無特殊說明, get_attribute 裏面使用的屬性名稱和 uiautomatorviewer 裏面的一致):

獲取不到,但會顯示在 uiautomatorviewer 中的屬性:

  • index
  • package
  • password
  • bounds(可通過 get_position 來獲取其中部分內容)

發佈了18 篇原創文章 · 獲贊 64 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章