appium get_attribute 方法

来源:互联网 发布:安卓录屏软件 编辑:程序博客网 时间:2024/05/01 00:49

问题描述

当使用类似下面的代码获取元素的 content-desc 属性时,会报 NoSuchElement 错误:

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

但使用如下代码却能正常执行:

# pythonself.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 里面的一致):

  • enabled
  • checkable
  • checked
  • clickable
  • focusable
  • focused
  • longClickable(返回 long-clickable)
  • scrollable
  • selected
  • displayed(此元素是否在当前界面存在。调用的是 UIObject 的 exists() 方法,详情请看http://developer.android.com/reference/android/support/test/uiautomator/UiObject.html#exists())

获取不到,但会显示在 uiautomatorviewer 中的属性:

  • index
  • package
  • password
  • bounds(可通过 get_position 来获取其中部分内容)
0 0
原创粉丝点击