Appium 用了 AppiumDriver 后,WebDriverWait 中无法使用 AppiumDriver 特有的方法

kuroky · 2014年08月01日 · 最后由 菜菜鸟 回复于 2018年03月09日 · 2992 次阅读
本帖已被设为精华帖!

用了 AppiumDriver 后,WebDriverWait 中无法使用 AppiumDriver 特有的方法,比如 findElementsByAndroidUIAutomator 等。这是由于
WebDriverWait 继承与 FluentWait,而 WebDriver 接口是没有定义 findElementsByAndroidUIAutomator 的,所以如果想用类似 WebDriverWait 的功能,就必须做些封装。

package com.merlini.app.common;

import io.appium.java_client.AppiumDriver;

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.NotFoundException;
import org.openqa.selenium.support.ui.Clock;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.Sleeper;
import org.openqa.selenium.support.ui.SystemClock;
import org.openqa.selenium.support.ui.WebDriverWait;

import com.merlini.common.Config;


public class AppiumDriverWait extends FluentWait<AppiumDriver>{
    //默认轮询时间(毫秒)
    public final static long DEFAULT_POLLINGEVERY_TIMEMILLS = Integer.parseInt(Config.getConfBykey("wait.sleepInMillis"));
    public final static long DEFAULT_TIMEOUT_INSECONDS = Integer.parseInt(Config.getConfBykey("wait.TimeOutInSeconds"));
    /**
       * Wait will ignore instances of NotFoundException that are encountered (thrown) by default in
       * the 'until' condition, and immediately propagate all others.  You can add more to the ignore
       * list by calling ignoring(exceptions to add).
       *
       * @param driver The AppiumDriver instance to pass to the expected conditions
       * @see WebDriverWait#ignoring(java.lang.Class)
       */
    public AppiumDriverWait(AppiumDriver driver) {
        this(driver, new SystemClock(), Sleeper.SYSTEM_SLEEPER, DEFAULT_TIMEOUT_INSECONDS, DEFAULT_POLLINGEVERY_TIMEMILLS);
    }
    /**
       * Wait will ignore instances of NotFoundException that are encountered (thrown) by default in
       * the 'until' condition, and immediately propagate all others.  You can add more to the ignore
       * list by calling ignoring(exceptions to add).
       *
       * @param driver The AppiumDriver instance to pass to the expected conditions
       * @param timeOutInSeconds The timeout in seconds when an expectation is called
       * @see WebDriverWait#ignoring(java.lang.Class)
       */
    public AppiumDriverWait(AppiumDriver driver, long timeOutInSeconds) {
        this(driver, new SystemClock(), Sleeper.SYSTEM_SLEEPER, timeOutInSeconds, DEFAULT_POLLINGEVERY_TIMEMILLS);
    }
    /**
       * Wait will ignore instances of NotFoundException that are encountered (thrown) by default in
       * the 'until' condition, and immediately propagate all others.  You can add more to the ignore
       * list by calling ignoring(exceptions to add).
       *
       * @param driver The WebDriver instance to pass to the expected conditions
       * @param timeOutInSeconds The timeout in seconds when an expectation is called
       * @param sleepInMillis The duration in milliseconds to sleep between polls.
       * @see WebDriverWait#ignoring(java.lang.Class)
       */
    public AppiumDriverWait(AppiumDriver driver, long timeOutInSeconds, long sleepInMillis) {
        this(driver, new SystemClock(), Sleeper.SYSTEM_SLEEPER, timeOutInSeconds, sleepInMillis);
      }
    protected AppiumDriverWait(AppiumDriver driver, Clock clock, Sleeper sleeper, long timeOutInSeconds,
          long sleepTimeOut) {
        super(driver, clock, sleeper);
        withTimeout(timeOutInSeconds, TimeUnit.SECONDS);
        pollingEvery(sleepTimeOut, TimeUnit.MILLISECONDS);
        ignoring(NotFoundException.class);
    }


}


package com.merlini.app.common;

import io.appium.java_client.AppiumDriver;

import com.google.common.base.Function;

public interface AppiumExpectedCondition<T> extends Function<AppiumDriver,T>{

}

这样就可以 AppiumDriverWait 代替 WebDriverWait
如:

/**
     * 根据控件description抓取批量元素
     * @param elementType
     * @param desc
     * @return
     */
    public List<WebElement> findElementsByDescription(final String elementType,final String desc){
        AppiumDriverWait wait=new AppiumDriverWait(driver);
        List<WebElement> ele= wait.until( new AppiumExpectedCondition<List<WebElement>>(){
            public List<WebElement> apply(AppiumDriver driver){
                return driver.findElementsByAndroidUIAutomator("new UiSelector().className(\"android.widget."+elementType+"\").description(\""+desc+"\")");
            }
        });
        return ele;//driver.findElementsByAndroidUIAutomator("new UiSelector().className(\"android.widget."+elementType+"\").description(\""+desc+"\")"); 
    }
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 9 条回复 时间 点赞
1楼 已删除

apply 中使用 By 可能可以解决,不要直接使用 WebDriver/AppiumDriver

没有 By.AndroidUIAutomator 的,难道 BY 有针对 AppiumDriver 的封装?

#3 楼 @kuroky

之前我在源码剖析帖里就有提及
mobileby 类扩展了之前的 By 类。

#!/usr/bin/env python

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from selenium.webdriver.common.by import By


class MobileBy(By):
    IOS_UIAUTOMATION = '-ios uiautomation'
    ANDROID_UIAUTOMATOR = '-android uiautomator'
    ACCESSIBILITY_ID = 'accessibility id'

试试看这个靠不靠普 ,啥时候加到加到接口里面就好了 ,还有那个 mobileElement 用起来相当不便

#5 楼 @kuroky

拿 expected_conditions 中的 presence_of_element_located 举例来说
webdriver.support 中的 expected_conditions 中定义的该可调用类中直接调用的就是核心的 find_element 方法
而 AppiumDriver 定义的那些 Find 方法调用的正是这个核心方法,只不过他扩展了 By 类,所以,这边是可以通过访问 MobileBy 的相关属性来实现 smartwait 的

class presence_of_all_elements_located(object):
    """ An expectation for checking that there is at least one element present
    on a web page.
    locator is used to find the element
    returns the list of WebElements once they are located
    """
    def __init__(self, locator):
        self.locator = locator

    def __call__(self, driver):
        return _find_elements(driver, self.locator)
def _find_elements(driver, by):
    try :
        return driver.find_elements(*by)
    except WebDriverException as e:
        raise e

#3 楼 @kuroky
有,MobileBy 方法返回的就是 By,可以把 appiumdriver 封装的 accessibilityid 方法拆出来。
java MobileBy 源码:

package io.appium.java_client;

import org.openqa.selenium.By;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebElement;

import java.io.Serializable;
import java.util.List;

/**
 * Created by jonahss on 4/10/14.
 */
public abstract class MobileBy extends By {

  public static By IosUIAutomation(final String uiautomationText) {
    if (uiautomationText == null) {
      throw new IllegalArgumentException("Must supply an iOS UIAutomation string");
    }

    return new ByIosUIAutomation(uiautomationText);
  }

  public static By AndroidUIAutomator(final String uiautomatorText) {
    if (uiautomatorText == null) {
      throw new IllegalArgumentException("Must supply an Android UIAutomator string");
    }

    return new ByAndroidUIAutomator(uiautomatorText);
  }

  public static By AccessibilityId(final String id) {
    if (id == null) {
      throw new IllegalArgumentException("Must supply a uiautomationText");
    }

    return new ByAccessibilityId(id);
  }

  public static class ByIosUIAutomation extends By implements Serializable {

    private final String automationText;

    public ByIosUIAutomation(String uiautomationText)   {
      automationText = uiautomationText;
    }

    @Override
    public List<WebElement> findElements(SearchContext context) {
      return ((FindsByIosUIAutomation) context).findElementsByIosUIAutomation(automationText);
    }

    @Override
    public WebElement findElement(SearchContext context) {
      return ((FindsByIosUIAutomation) context).findElementByIosUIAutomation(automationText);
    }

    @Override
    public String toString() {
      return "By.IosUIAutomation: " + automationText;
    }
  }

  public static class ByAndroidUIAutomator extends By implements Serializable {

    private final String automatorText;

    public ByAndroidUIAutomator(String uiautomatorText) {
      automatorText = uiautomatorText;
    }

    @Override
    public List<WebElement> findElements(SearchContext context) {
      return ((FindsByAndroidUIAutomator) context).findElementsByAndroidUIAutomator(automatorText);
    }

    @Override
    public WebElement findElement(SearchContext context) {
      return ((FindsByAndroidUIAutomator) context).findElementByAndroidUIAutomator(automatorText);
    }

    @Override
    public String toString() { return "By.AndroidUIAutomator: " + automatorText; }
  }

  public static class ByAccessibilityId extends By implements Serializable {

    private final String id;

    public ByAccessibilityId(String id)   {
      this.id = id;
    }

    @Override
    public List<WebElement> findElements(SearchContext context) {
      return ((FindsByAccessibilityId) context).findElementsByAccessibilityId(id);
    }

    @Override
    public WebElement findElement(SearchContext context) {
      return ((FindsByAccessibilityId) context).findElementByAccessibilityId(id);
    }

    @Override
    public String toString() {
      return "By.AccessibilityId: " + id;
    }
  }
}



Config.getConfBykey("wait.sleepInMillis");
这是个什么东西啊?我的编译器不认识 Config.getConfBykey 这个东东

#8 楼 @shimazakiharuka 这个是配置信息,是个是数字

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册