Appium 切换到 webview 后,无论是获取 element,还是 pagesource 都无响应。

lim · 2015年04月19日 · 最后由 扮猪吃老虎 回复于 2015年05月20日 · 1810 次阅读

多看 app 做测试。切换到 webview 后,无论是获取 element,还是 pagesource 都无响应。
手机 4.4.4 的系统。不知道是什么原因。。日志是说,Didn't get a new command in 60 secs, shutting down...

private AppiumDriver driver;

 public void setUp() throws Exception {
     // set up appium
     DesiredCapabilities capabilities = new DesiredCapabilities();
     capabilities.setCapability("deviceName","af68ec7");
     capabilities.setCapability("platformVersion", "4.4.4");
     capabilities.setCapability("appPackage", "com.duokan.reader");
     capabilities.setCapability("appActivity", ".DkMainActivity");
     driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
 }

 public void test() {
     System.out.println(driver.getContextHandles());
     driver.context("WEBVIEW_com.duokan.reader");
     driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
     System.out.println(driver.getPageSource());
}

info: [debug] Available contexts: NATIVE_APP,WEBVIEW_com.eg.android.AlipayGphone,WEBVIEW_com.sankuai.meituan,WEBVIEW_com.duokan.reader
info: [debug] ["WEBVIEW_com.duokan.reader","WEBVIEW_com.sankuai.meituan","WEBVIEW_com.eg.android.AlipayGphone"]
info: [debug] Available contexts: NATIVE_APP,WEBVIEW_com.duokan.reader,WEBVIEW_com.sankuai.meituan,WEBVIEW_com.eg.android.AlipayGphone
info: [debug] Connecting to chrome-backed webview
info: [debug] Creating Chrome session
info: [debug] Set chromedriver binary as: C:\Program Files (x86)\Appium\node_modules\appium\build\chromedriver\windows\chromedriver.exe
info: [debug] Ensuring Chromedriver exists
info: [debug] Killing any old chromedrivers, running: FOR /F "usebackq tokens=5" %a in (netstat -nao ^| findstr /R /C:"9515 ") do (FOR /F "usebackq" %b in (TASKLIST /FI "PID eq %a" ^| findstr /I chromedriver.exe) do (IF NOT %b=="" TASKKILL /F /PID %b))
info: [debug] No old chromedrivers seemed to exist
info: [debug] Spawning chromedriver with: C:\Program Files (x86)\Appium\node_modules\appium\build\chromedriver\windows\chromedriver.exe
info: [debug] [CHROMEDRIVER] Starting ChromeDriver 2.15.322448 (52179c1b310fec1797c81ea9a20326839860b7d3) on port 9515
Only local connections are allowed.
info: [debug] Making http request with opts: {"url":"http://127.0.0.1:9515/wd/hub/session","method":"POST","json":{"sessionId":null,"desiredCapabilities":{"chromeOptions":{"androidPackage":"com.duokan.reader","androidUseRunningApp":true,"androidDeviceSerial":"af68ec7"}}}}
info: [debug] Didn't get a new command in 60 secs, shutting down...
info: Shutting down appium session
info: [debug] Pressing the HOME button
info: [debug] executing cmd: C:\Users\liming\AppData\Local\Android\sdk\platform-tools\adb.exe -s af68ec7 shell "input keyevent 3"
info: [debug] Stopping logcat capture
info: [debug] Logcat terminated with code null, signal SIGTERM
info: [debug] [BOOTSTRAP] [debug] Got data from client: {"cmd":"shutdown"}
info: [debug] [BOOTSTRAP] [debug] Got command of type SHUTDOWN
info: [debug] [BOOTSTRAP] [debug] Returning result: {"value":"OK, shutting down","status":0}
info: [debug] [BOOTSTRAP] [debug] Closed client connection
info: [debug] [UIAUTOMATOR STDOUT] INSTRUMENTATION_STATUS: numtests=1
info: [debug] [UIAUTOMATOR STDOUT] INSTRUMENTATION_STATUS: stream=.
info: [debug] [UIAUTOMATOR STDOUT] INSTRUMENTATION_STATUS: id=UiAutomatorTestRunner
info: [debug] [UIAUTOMATOR STDOUT] INSTRUMENTATION_STATUS: test=testRunServer
info: [debug] [UIAUTOMATOR STDOUT] INSTRUMENTATION_STATUS: class=io.appium.android.bootstrap.Bootstrap
info: [debug] [UIAUTOMATOR STDOUT] INSTRUMENTATION_STATUS: current=1
info: [debug] [UIAUTOMATOR STDOUT] INSTRUMENTATION_STATUS_CODE: 0
info: [debug] [UIAUTOMATOR STDOUT] INSTRUMENTATION_STATUS: stream=
info: [debug] [UIAUTOMATOR STDOUT] Test results for WatcherResultPrinter=.
info: [debug] [UIAUTOMATOR STDOUT] Time: 81.623
info: [debug] [UIAUTOMATOR STDOUT] OK (1 test)
info: [debug] [UIAUTOMATOR STDOUT] INSTRUMENTATION_STATUS_CODE: -1
info: [debug] Sent shutdown command, waiting for UiAutomator to stop...
info: [debug] UiAutomator shut down normally
info: [debug] Cleaning up android objects
info: [debug] Cleaning up appium session
info: [debug] We shut down because no new commands came in
共收到 35 条回复 时间 点赞

从日志上看,chromedriver 起来后 appium server 就没有收到 client 的任何请求(卡在 chromedriver 的开 session 这里了,appium 发送了开 session 的请求后 chromedriver 一直没有反应),所以 session timeout 退出了。
原因应该是因为切换 context 这个请求一直没有返回消息给 client 端,所以 client 一直停在切换 context 那一行等待 server 的返回消息,即切换 context 后的所有代码都没有执行过,所以你说的找元素和取源码都没有效果。

建议的解决方案:

  1. 根据 chromedriver 的 Dependencies 和 Device Requirements 检查你的测试环境和被测应用是否符合要求:https://sites.google.com/a/chromium.org/chromedriver/getting-started/getting-started---android
  2. 检查你的参数配置是否有错,特别是 deviceName ,在 chromedriver 里面 deviceName(androidDeviceSerial )是用来选择特定设备的,必须和设备的序列号一致,如果你是单个 android 设备可以试试去掉 deviceName 这个参数。
  3. 升级 chromedriver 到最新版后直接在脚本里用 chromedriver 启动 session ,看到底出现什么错误。
lim #1 · 2015年04月20日 Author

#1 楼 @chenhengjie123 多看的 app 是从官网上下载安装后,拿来做的测试。刚刚写了个 webview demo 进行了测试,发现是可以正确获得元素相关信息的。不知道跟 app 本身的编译方式或者设置有关系?

那可能是多看的 app 没有开 web debug 了。

想问下有没有什么办法能升级 app 里内嵌的浏览器呢?

#1 楼 @chenhengjie123
app 里内嵌的浏览器有什么办法能升级吗?

升级是指使用其他浏览器?
你可以看看 crosswalk,把 chrome 内核打包到 app 中。

#6 楼 @chenhengjie123 这个是测试 app 里的浏览器.
手机浏览器能升级; Android 内嵌 weibview 的浏览器 怎么来升级

#7 楼 @will_lee 据我所知,升级不了。webview 默认使用的浏览器是系统自带的,它不是以应用形式存在,只能通过升级系统来升级 android 内置 webview 浏览器。
你可以通过把浏览器内核打包到应用中来使用自己想用的版本的浏览器,前面我提到的 crosswalk 做的就是这个事情。缺点是应用体积会大不少。

@aicdlychee @chenhengjie123 问题解决了没?
我这里也遇到了一样的问题~,我是 Appium1.3.1,然后替换的 ChromeDriver.exe 是 2.14 版本,设备是魅族 MX4 PRO
Web Debug 选项是打开的。

#9 楼 @weamylady 我这里没出现过这个问题哦。
你也是 chromedriver 起来后没反应?你试试直接用 chromedriver ,不用 appium 看有没有问题?
正常来说用 chrome 浏览器能看到的 webview chromedriver 应该都能控制。

#10 楼 @chenhengjie123 ChromeDriver 是有响应的,只是不回应定位元素的 Post:

info: --> POST /wd/hub/session/e386794d-1bb1-445c-872e-9410ccaf0d69/timeouts/imp
licit_wait {"ms":4000}
info: [debug] Proxying command to 127.0.0.1:9515
info: [debug] Making http request with opts: {"url":"http://127.0.0.1:9515/wd/hu
b/session/ae767a83315371eceaf0d4c3c2a00d3d/timeouts/implicit_wait","method":"POS
T","json":{"ms":4000}}
info: [debug] Proxied response received with status 200: {"sessionId":"ae767a833
15371eceaf0d4c3c2a00d3d","status":0,"value":null}
info: <-- POST /wd/hub/session/e386794d-1bb1-445c-872e-9410ccaf0d69/timeouts/imp
licit_wait 200 10.676 ms - 72
info: --> POST /wd/hub/session/e386794d-1bb1-445c-872e-9410ccaf0d69/element {"us
ing":"xpath","value":"//*[starts-with(translate(@text,' ',''),'取款密码') or sta
rts-with(translate(@content-desc,' ',''),'取款密码') or starts-with(translate(te
xt(),' ',''),'取款密码')][1]/following::*[1]"}
info: [debug] Proxying command to 127.0.0.1:9515
info: [debug] Making http request with opts: {"url":"http://127.0.0.1:9515/wd/hu
b/session/ae767a83315371eceaf0d4c3c2a00d3d/element","method":"POST","json":{"usi
ng":"xpath","value":"//*[starts-with(translate(@text,' ',''),'取款密码') or star
ts-with(translate(@content-desc,' ',''),'取款密码') or starts-with(translate(tex
t(),' ',''),'取款密码')][1]/following::*[1]"}}
info: [debug] Didn't get a new command in 60 secs, shutting down...

那我就不清楚了。
你试下去搜索 chromedriver ,看它不回应时从哪里能找到一些 log 信息?
或者直接用 chromedriver 来运行你的测试试试?

#12 楼 @chenhengjie123 问你个问题,在真机 Android4.3 机器上,看不到 WEBVIEW Context 怎么搞?setWebContentsDebuggingEnabled 只对 Android4.4 以上有用哦……

#13 楼 @weamylady 只能用 Selendroid 模式。automationName:selendroid
chromedriver 只能测 4.4 以上。4.4 以下要用 Selendroid 。

#14 楼 @chenhengjie123 用了 Selendroid 之后我就识别不了基本的 Native 元素了。我用的是 Xpath。使用 Selendroid 有什么要注意的吗?我已经设置了 grunt setConfigVer:selendroid。

#15 楼 @weamylady 因为 selendroid 是基于 Instrumentation 的,而 appium 默认是基于 uiautomator ,所以两者获取回来的界面 xml 结构会有所不同,用 appium 时能定位的 xpath 在 selendroid 下可能会获取不了(xml 结构不一样了)。
我目前项目里没怎么用过 selendroid 模式,所以有什么要注意还真不大清楚哈。

#16 楼 @chenhengjie123 您有没有遇到过 hybird 的 android app context 不返回 webview?我们的 app 是 hybird 但是有一个就是不返回 webview,只有 native_app 我是连真机 4.4.2

#17 楼 @irisliu 我自己没有,但我认识的人有。而且他用 remote debug 也看不到(他用的 app 在我的环境下可以看到 webview 的),最后解决方案是换手机。
可能性有两个,一个是 app 里禁用了 remote debug ,另一个是那台手机的系统本身有问题。
你试一下:

  1. remote debug 能否看到?看不到的话要不是禁用了 remote debug ,要不是 app 用的 webview 不是用 chronium 内核的。
  2. remote debug 能看到。升级一下 chromedriver 再试试。

#16 楼 @chenhengjie123 确实是坑,在 Selendroid 模式下需要用@Value来定位,而不是用@Text。另外我现在用的 Appium 上 Selendroid 有 bug,在新的 0.15 版本上有 fix。
https://github.com/selendroid/selendroid/issues/309#issuecomment-36837808
不知道 Appium1.4 会不会使用 Selendroid0.15 以上的版本捏?~

#19 楼 @weamylady 目前我所知道的在 selendroid 模式下,除了要用 Value 来定位,ByID 的格式也不一样,要去掉 com.xxx.xxx:id/ ,而且 swip 方法在 4.2 及以下设备上失效(可以通过 adb shell input touchscreen swipe 绕过),scoll 方法失效。就连页面上的 textview 和 imageview 有时都会变成 Novatextview 和 Novaimageview,到处都是坑。

#20 楼 @sunrise 最大的坑还是整个页面都识别不出来……据说是因为 Selendroid 有 bug,页面上元素带有 $ 符号就会生成不了 xml 之类的,这个问题我现在还在愁呢!

#18 楼 @chenhengjie123 确实是 debug 没开 代码里加了 WebView.setWebContentsDebuggingEnabled(true) 就可以了 多谢了

#21 楼 @weamylady 是的,我也遇到了,有些页面我打印 page_source 直接报错了

#18 楼 @chenhengjie123 请问 hybrid app,用 python 实现 hybrid 切换到 webview 怎么弄呢?
我这样尝试的

#coding=utf-8

import os
import unittest,sys,time,re,datetime,HTMLTestRunner
from appium import webdriver
from time import sleep
from appium.common.exceptions import NoSuchContextException
import sys
from selenium.webdriver.support.ui import WebDriverWait

#reload(sys)
#sys.setdefaultencoding("utf-8") 

# Returns abs path relative to this file and not cwd
PATH = lambda p: os.path.abspath(
    os.path.join(os.path.dirname(__file__), p)
)

class ContactsAndroidTests(unittest.TestCase):
    def setUp(self):
        desired_caps = {}
        desired_caps['platformName'] = 'Android'
        desired_caps['platformVersion'] = '4.4'
        desired_caps['deviceName'] = '192.168.56.101:5555'
        '''desired_caps['app'] = PATH(
            '../../../sample-code/apps/ContactManager/ContactManager.apk'
        )'''
        desired_caps['appPackage'] = 'com.jiudao.ccare'
        desired_caps['appActivity'] = '.MainActivity'
        desired_caps["unicodeKeyboard"] = "True"
        desired_caps["resetKeyboard"] = "True"


        self.driver = webdriver.Remote('http://192.168.10.177:4723/wd/hub', desired_caps)
        #self.driver.implicitly_wait(1)

    def login(self):
        #登录操作
        #time.sleep(3)
        self.driver.implicitly_wait(10)
        a=self.driver.contexts
        print (a)
        self.driver.switch_to.context("pt1.1")

if __name__ == '__main__':
    suite = unittest.TestSuite()
    suite.addTest(ContactsAndroidTests("login"))
    timestr = time.strftime('%Y%m%d%H%M%S',time.localtime(time.time()))
    filename = "D:\\appium\\appiumresult\\result_" + timestr + ".html"
    print (filename)
    fp = open(filename, 'wb')
    runner = HTMLTestRunner.HTMLTestRunner(
                stream=fp,
                title='测试结果',
                description='测试报告'
                )
    runner.run(suite)
    #g_browser.quit()
    fp.close() #测试报告关闭

这是我获得的测试报告,我 print 出来的东西是 pt1.1

#18 楼 @chenhengjie123
实际上我切换的代码就三行

self.driver.implicitly_wait(10)
a=self.driver.contexts
print (a)

#23 楼 @sunrise 这个问题你解决了吗?

#26 楼 @weamylady 暂时无解,只能用 uiautomation view 来查看元素

#27 楼 @sunrise 我指的不仅仅是查看元素,而是定位元素的时候就有问题,直接报 500 Undefined~

#28 楼 @weamylady 嗯,page_source 都获取不到,自然也就没法定位。我是用 uiautomator 获取元素坐标,再用 adb shell input tap 点击的,这样可以绕过。还是希望 selendroid 能解决这个 bug 吧。

def __uidump(self):
    #获取当前Activity控件树
    self.tempFile = tempfile.gettempdir()
    self.pattern = re.compile(r"\d+") 
    xml = "/data/local/tmp/uidump.xml"
    os.popen("adb -s {0} shell uiautomator dump {1}".format(self.device_id, xml))
    os.popen("adb -s {0} pull {1} {2}".format(self.device_id, xml, self.tempFile))

def get_element_point(self, attrib, name, nub):
    """
    根据元素查找坐标,返回的坐标为元素中心点
    传入元素类型、名称、下标,其中有多个元素时通过下标控制,默认为0

    :Usage:
        self.__element("resource-id","scorebar",0)
    """      
    self.__uidump()
    x = []
    y = []
    tree = ET.ElementTree(file=os.path.join(self.tempFile, "uidump.xml"))
    treeIter = tree.iter(tag="node")
    for elem in treeIter:
        if elem.attrib[attrib] == name:
            bounds = elem.attrib["bounds"]
            coord = self.pattern.findall(bounds)
            Xpoint = (int(coord[2]) + int(coord[0])) / 2.0
            Ypoint = (int(coord[3]) + int(coord[1])) / 2.0
            x.append(Xpoint)
            y.append(Ypoint)
    return x[nub], y[nub]          

#29 楼 @sunrise 腻害!果然好暴力的做法~ 3~ 我们的策略是先支持 Android4.4 以上的,4.3 以下的只有一台机器就先不支持了。

Appium 中,selendroid 模式与 uiautomator 模式使用上最大的差别还是定位规则不统一。
selendroid 模式并未完全规范化的支持 xpath,所以定位起来跟 uiautomator 模式相差甚远没,甚至 contains 这样的语法都不能用。
关于 ById 的定位方式,从 Appium@1.4.0 之后估计就是一样的了。

然后,我就想问问,挖掘机技术哪家强!?

啊呸,为什么我的 4.4 系统,uiautomator 模式下获取不到 webview 的 context!?selendroid 很正常啊。

#3 楼 @chenhengjie123 请问下这一句 WebView.setWebContentsDebuggingEnabled(true),在 python 里应该怎么来写啊~~ 我也是切换到 webview 后,就定位不到元素了. 不知道在 python 里这个方法应该怎么来实现~~多谢啊

#32 楼 @will_lee 这句是写到被测 app 里面的,不是测试脚本。。。

#31 楼 @anikikun selendroid 模式和 uiautomator 模式不一样主要还是因为它们的实现完全不一样。
selendroid 底层是用 Instrumentation 的,dump 出来的结构是 hierarchyview 看到的结构
uiautomator 模式底层用的是 uiautomator ,dump 出来的结构是 uiautomatorviewer 看到的结构

你在同一个界面用两个不同模式 log source 看下就知道了,出来的东西完全不一样,所以 xpath 自然不一样。

#33 楼 @chenhengjie123 如果我没有被测 app 的源代码,只能拿到 apk 的情况下,没有开 web debug,这该怎么处理啊

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