测试目的:

在鼓捣 atx 基于 python 的 uiautomator2 的 toast 消息提示。之前看到过基于 appium uiautomator2 的 toast 消息的捕获方案。
所以得先弄懂,弄通,以致 debug 通 appium uiautomator2 的 toast 消息的原理与调用的链路和包关系。

预置条件:

在 ATX363 python3.6.3 虚拟环境中 安装 Appium-Python-Client

(ATX363) cmd@TR:~$ pip install Appium-Python-Client
Collecting Appium-Python-Client
  Downloading Appium-Python-Client-0.26.tar.gz
Collecting selenium>=2.47.0 (from Appium-Python-Client)
  Downloading selenium-3.8.1-py2.py3-none-any.whl (942kB)
    100% |████████████████████████████████| 952kB 44kB/s 
Installing collected packages: selenium, Appium-Python-Client
  Running setup.py install for Appium-Python-Client ... done
Successfully installed Appium-Python-Client-0.26 selenium-3.8.1

再安装 selenium 发现已经提示安装好了,应该是 Appium-Python-Client 关联安装的

(ATX363) cmd@TR:~$ pip install selenium
Requirement already satisfied: selenium in ./.pyenv/versions/3.6.3/envs/ATX363/lib/python3.6/site-packages

查看下目前 ATX363 虚拟环境下已安装的 python 第三方库信息和版本

(ATX363) cmd@TR:~$ pip list
DEPRECATION: The default format will switch to columns in the future. You can use --format=(legacy|columns) (or define a format=(legacy|columns) in your pip.conf under the [list] section) to disable this warning.
aircv (1.4.6)
Appium-Python-Client (0.26)
atx (1.1.3.dev36)
atx-uiautomator (0.3.3)
AxmlParserPY (0.0.3)
certifi (2018.1.18)
chardet (3.0.4)
colorama (0.3.9)
decorator (4.2.1)
facebook-wda (0.2.2.dev1)
fire (0.1.2)
futures (3.0.5)
humanize (0.5.1)
idna (2.6)
imageio (2.2.0)
maproxy (0.0.12)
numpy (1.14.0)
opencv-contrib-python (3.4.0.12)
Pillow (5.0.0)
pip (9.0.1)
progress (1.3)
py (1.5.2)
PyYAML (3.11)
requests (2.18.4)
retry (0.9.2)
selenium (3.8.1)
setuptools (28.8.0)
six (1.11.0)
tornado (5.0a1)
tqdm (4.5.0)
uiautomator2 (0.0.4.dev1)
urllib3 (1.22)
weditor (0.0.4.dev6)

USB 连接我的小米 mix2 真机 ,开启手机开发者选项,开始 usb 调试模式和其它相关。

cmd@TR:~$ adb devices -l
List of devices attached
yournumber               device usb:3-1 product:chiron model:MIX_2 device:chiron

向手机安装百度阅读 apk

cmd@TR:~/app$ adb install baiduyuedu_5150.apk
Success

犯贱又初始化向手机安装了 atx 的 uiautomator2 相关手机端

(ATX363) cmd@TR:~$ python -m uiautomator2 init
2018-01-27 10:08:39,404 - __main__.py:241 - INFO - Device(45806625) initialing ...
2018-01-27 10:08:39,646 - __main__.py:120 - INFO - install minicap
2018-01-27 10:08:39,768 - __main__.py:127 - INFO - install minitouch
2018-01-27 10:08:39,980 - __main__.py:142 - INFO - apk(1.0.9) already installed, skip
2018-01-27 10:08:41,310 - __main__.py:162 - INFO - atx-agent(0.1.5) already installed, skip
2018-01-27 10:08:44,575 - __main__.py:211 - INFO - atx-agent output: server started, listening on 172.25.69.55:7912
2018-01-27 10:08:44,575 - __main__.py:212 - INFO - success

检查 appium 版本和开启 appium 服务

cmd@TR:~$ appium -v
1.7.2
cmd@TR:~$ appium-doctor
info AppiumDoctor Appium Doctor v.1.4.3
info AppiumDoctor ### Diagnostic starting ###
info AppiumDoctor  ✔ The Node.js binary was found at: /opt/nodejs/bin/node
info AppiumDoctor  ✔ Node version is 6.10.3
info AppiumDoctor  ✔ ANDROID_HOME is set to: /opt/android-sdk-linux
info AppiumDoctor  ✔ JAVA_HOME is set to: /usr/lib/jvm/java-8-oracle
info AppiumDoctor  ✔ adb exists at: /opt/android-sdk-linux/platform-tools/adb
info AppiumDoctor  ✔ android exists at: /opt/android-sdk-linux/tools/android
info AppiumDoctor  ✔ emulator exists at: /opt/android-sdk-linux/tools/emulator
info AppiumDoctor  ✔ Bin directory of $JAVA_HOME is set
info AppiumDoctor ### Diagnostic completed, no fix needed. ###
info AppiumDoctor 
info AppiumDoctor Everything looks good, bye!
info AppiumDoctor 

cmd@TR:~$ appium --session-override -p 4730
[Appium] Welcome to Appium v1.7.2
[Appium] Non-default server args:
[Appium]   port: 4730
[Appium]   sessionOverride: true
[Appium] Appium REST http interface listener started on 0.0.0.0:4730

编辑脚本:

使用 pycharm,先去 File-> Settings-> Project -> Project Interpreter -> Add Local
在 Existing environment 中选择/home/cmd/.pyenv/versions/ATX363/bin/python 切换到 ATX363 虚拟环境,以便 import 到在该环境安装的 appium python clinet 和 selenium 包。

编辑 FindToastTest.py 脚本,内容如下

# coding:utf-8
from appium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.common.by import By
import unittest
import time

class FindToastTest(unittest.TestCase):

    def setUp(self):
        desired_caps = {"platformName": "Android",
                        "platformVersion": "7.1.1",
                        "deviceName": "MIX_2",
                        "udid": "yournumber",
                        "appPackage": "com.baidu.yuedu",
                        "appActivity": "com.baidu.yuedu.splash.SplashActivity",
                        "noReset": True,
                        'newCommandTimeout': 30,
                        "unicodeKeyboard": True,
                        "resetKeyboard": True,
                        'automationName': 'Uiautomator2',
                        "noSign": True}

        remote = "http://localhost:4730/wd/hub"
        self.d = webdriver.Remote(remote, desired_caps)


    def is_toast_exist(driver,toastmessage,timeout=30,poll_frequency=0.5):
        '''is toast exist, return True or False
        :Agrs:
         - driver - 传driver
         - toastmessage   - 页面上看到的toast消息文本内容
         - timeout - 最大超时时间,默认30s
         - poll_frequency  - 间隔查询时间,默认0.5s查询一次
        :Usage:
         is_toast_exist(driver, "toast消息的内容")
        '''
        try:
            toast_loc = ("xpath", ".//*[contains(@text,'%s')]" % toastmessage)
            WebDriverWait(driver, timeout, poll_frequency).until(expected_conditions.presence_of_element_located(toast_loc))
            return True
        except:
            return False

#       element = WebDriverWait(driver,timeout,poll_frequency).until(expected_conditions.presence_of_element_located((By.PARTIAL_LINK_TEXT,message)))


    def test_toast(self):
        #等待主页面activity出现
        self.d.wait_activity(".base.ui.MainActivity", 10)
        # 点击返回,退出app
        self.d.back()
        toastresult = self.is_toast_exist(self.d,'百度阅读:再按一次退出')
        if toastresult is True:
            print('Test Success find toast message')
        else:
            print('Test Failed not find toast message')



    def tearDown(self):
        try:
            self.d.quit()
        except:
            pass

if __name__ == '__main__':
    try:
        suite = unittest.TestLoader().loadTestsFromTestCase(FindToastTest)
        unittest.TextTestRunner(verbosity=2).run(suite)
    except SystemExit:
        pass

开始测试:

运行该脚本 已经安装有 atx-uiautomator2 的手机,连接 的 appium server 会报错
手机端卸载掉 uiautomator2 后,可以正常跑 FindToastTest.py 期间手机端弹出提示安装了 io.appium.uiautomator2.server 和 io.appium.uiautomator2.server.test
(当然如果是第一次用该手机的话还会提示安装 Unlock 和 Appium Setting )

参考内容:

参考了以下前辈和大神的方案,站在巨人的肩膀上,能让我们走的更远,感谢以下人士的不懈努力和分享。
http://blog.csdn.net/songer_xing/article/details/71272566

final WebDriverWait wait = new WebDriverWait(driver,3);
Assert.assertNotNull(wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath(".//*[contains(@text,'"+ toast + "')]"))));
log.info("查找toast成功!");
return true;
} catch (Exception e) {
throw new AssertionError("找不到"+toast);

https://mp.weixin.qq.com/s?__biz=MzI5ODU1MzkwMA==&mid=2247484294&idx=1&sn=d860af3f4b4049d42f7f95cd086400e9&chksm=eca544c5dbd2cdd3b5810fa7f5d1316352266c45df093c0cc80ddac4e5f008de68398af29344&mpshare=1&scene=23&srcid=12259hUg47FJIQGCd7OEV9Dt#rd

def is_toast_exist(driver,text,timeout=30,poll_frequency=0.5):
   '''is toast exist, return True or False
   :Agrs:
    - driver - 传driver
    - text   - 页面上看到的文本内容
    - timeout - 最大超时时间,默认30s
    - poll_frequency  - 间隔查询时间,默认0.5s查询一次
   :Usage:
    is_toast_exist(driver, "看到的内容")
   '''
   try:
       toast_loc = ("xpath", ".//*[contains(@text,'%s')]"%text)
       WebDriverWait(driver, timeout, poll_frequency).until(EC.presence_of_element_located(toast_loc))
       return True
   except:
       return False

参考这个帖子http://testerhome.com/topics/2354 回帖中的 waitForElement,我自己写了一个 appium selendroid 模式下的 python 函数查找 toast

message消息内容
timeout超时时间
poll_frequency查询步长
driver就是driver

from

selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions

def find_toast(message,timeout,poll_frequency,driver):
    element = WebDriverWait(driver,timeout,poll_frequency).until(expected_conditions.presence_of_element_located((By.PARTIAL_LINK_TEXT,message)))

补充:

atx-uiautomator2 的 toast 消息也测试通过了
atx-uiautomator2 识别 android toast 测试


↙↙↙阅读原文可查看相关链接,并与作者交流