• 学习appium必备的避坑指南


    首先感谢白月黑羽imhelloworld提供的在线教程和视频教程。是你们的教程伴我度过了疫情期间的漫漫长夜。

    • 第一个坑:永远属于环境

    uiautomatorviewer:用来做APP界面定位用的。

    这个文件位于androidsdk\tools\bin\uiautomatorviewer.bat,但是从白月黑羽提供的网盘链接下载软件后,它没办法使用jdk11,需要改安装jdk8。可能是androidsdk版本比较老,不想太折腾,所以我也没有再尝试最新的androidsdk是否支持jdk11。

    附上环境变量(还需要有nodejs、python环境)设置:

    1. JAVA_HOME    C:\Program Files\Java\jdk1.8.0_211
    2. CLASSPATH    .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;
    3. ANDROID_HOME    D:\Programs\androidsdk
    4. path    D:\Programs\Python37\Scripts\;D:\Programs\Python37\;C:\Program Files\nodejs\;%JAVA_HOME%\bin;D:\Programs\androidsdk\platform-tools

    如果依旧报错,也有可能是androidsdk\tools\lib\find_java.bat文件问题,因为r17 以上的版本重写了这个文件,我们只需要把这个文件替换成r16的版本文件即可。老版本的find_java.bat可以从这下载:

    链接:https://pan.baidu.com/s/1ng2EeMJ_jjF_QAbNa0s5LQ 
    提取码:16b3

    androidsdk的下载可以直接去官网先下载Android Studio:Download Android Studio & App Tools - Android Developers

    然后通过Android Studio去下载sdk。也可以直接去这个网站下载:Download SDK Platform for Android SDK Manager

    • 第二个坑:adb devices报错

    我尝试了夜神模拟器,做得真不错!但是公司的笔记本配置还是6年前的,有点扯后腿,玩不转。后来我改用数据线连接真机了。需要说明的是,华为手机或者分家之前的荣耀手机,即使升级了HarmonyOS,照样还是可以使用安卓这一套来测试,我看网上很多人都说HarmonyOS底层还是安卓,大概多多少少和这也有点关系吧。

    不过从HarmonyOS的开发者文档来看,以及HarmonyOS万物互联的设计思路,我更多的是觉得安卓这块只是为了兼容,HarmonyOS应该是有自己的一套底层逻辑的,说到底,安卓和HarmonyOS都是在linux内核上面做文章。这就像是容器技术一样,一个linux内核,跑两个不同的docker容器,一个是安卓,一个是HarmonyOS,嗯,这么理解应该没错。(扯远了

    如果执行adb devices报错,需要将夜神模拟器中nox_adb.exe替换掉。先将Nox\bin\nox_adb.exe重命名为nox_adb.exe.bak备份下,再将androidsdk\platform-tools\adb.exe复制一份,改名为nox_adb.exe,直接放到Nox\bin\下。

    • 第三个坑:查找appPackage和appActivity

    建议直接连接真机或者模拟器输入命令。不建议通过apk文件来查找,原因在于:一个要考虑apk版本,一个是网上给的命令并不一定能查到结果。而且你最终还是程序还是要在真机或模拟器上面运行的。

    真机或模拟器连接成功,且打开APP的某页面。然后在cmd输入命令:

    1. 方法一
    2. adb shell dumpsys activity recents | find "intent={"
    3. 方法二
    4. adb shell dumpsys activity activities

    两个方法大同小异,方法二输出的信息更多,得自己过滤。

    • 第四个坑:python程序运行报错

    SyntaxError: Non-UTF-8 code starting with ‘\xba‘ in file

    这个报错我相信大部分人都不会遇到。只有我这种用着6年前配置的电脑,开不起一个Pycharm,只能在nodepad++上写代码才会遇到这样的辛酸。

    将程序文件在nodepad++打开后,全选复制一下,然后菜单栏编码处选择使用UTF-8编码,再粘贴覆盖,保存即可。

    • 第五个坑:执行 adb devices -l 命令找不到手机

    我的荣耀V20升级了HarmonyOS,为了防止意外,我特地翻出了那根原厂USB线(线皮都破了露出了铜丝),连接到笔记本后,颤抖着手,用管理员权限打开命令行窗口,输入了adb devices -l,然后给我下面的反馈:

    网上找了下原因,ADB Interface找不到驱动程序。

    通过 右击计算机-->点击 “管理” --> 找到 “设备管理器” --> 找到 “其他设备”:

    驱动程序有感叹号,得重新安装驱动程序。双击带黄色感叹号的ADB Interface更新驱动程序,选择Windows自动搜索更新驱动程序软件,结果发现驱动程序无法正常安装。手动选择电脑上的文件安装驱动程序,参考谷歌文档:

    安装原始设备制造商 (OEM) USB 驱动程序  |  Android 开发者  |  Android Developers

     我用的是荣耀,只能安装通用的驱动程序。可以使用文档中下载的驱动,也可以选择Android SDK中的驱动。

    •  第六个坑:error: device unauthorized

    重新拔插换个USB接口试试,如果还不行,看下报错信息,里面有这样一句话:

    Try 'adb kill-server' if that seems wrong.

    在命令行窗口输入adb kill-server 然后直接 adb devices -l 命令,手机上弹出了授权提示框,点击允许,这个问题就解决了!

    这个问题其实很简单,能从错误提示中解决。但是设备未授权的原因确实没找出来,而且不执行adb kill-server,手机上就不会弹出授权的对话框,所以容易浪费时间。

    • 第七个坑:来自无线调试的由于目标计算机积极拒绝,无法连接

    必须在开发者模式下勾选USB调试,和“仅充电”模式下允许ADB调试。手机有adb网络调试的也可以打开。

    无线调试不建议使用,我尝试下来,每次断开后都要重新设置一遍,还不如插上USB线来得快。

    • 第八个坑:调试程序时Original error: Could not proxy command to the remote server

    Encountered internal error running command: UnknownError: An unknown server-side error occurred while processing the command. Original error: Could not proxy command to the remote server. Original error: timeout of 240000ms exceeded

    在手机的设置——应用与服务——应用管理下,卸载掉自动安装的Appium Settings、io.appium.uiautomator2.server、io.appium.uiautomator2.server.test,然后手机关机重启,再次执行就正常了。该情况不会多次出现,一般只会出现一次。

    • 第九个坑:resetKeyboard=True

    resetKeyboard=True, # 执行完程序恢复原来输入法

    从两位自动化测试的前辈那里拿来的代码,注释上写的这段其实并不奏效。在测试完之后,还是需要在设置-高级设置-语言和输入法,然后把默认输入法改成自己常用的那个,这样手机在手动输入时,输入法才能调用出来。

    • 第十个坑:appium输出日志
    appium -g 日志文件名称.log

    我使用下来,这里是可以使用相对路径的。并且这个日志文件,不是使用append的方式写入的,而是每次使用时,都是覆盖上次的日志文件。所以如果需要保留上次的日志,日志文件的名称需要注意保持唯一性。

    • 第11个坑:python TypeError: 'module' object is not callable

     网上资料说:Python导入模块的方法有两种:import module 和 from module import,区别是前者所有导入的东西使用时需加上模块名的限定,而后者不要。

    我给的建议是,无论你是用import module 还是 from module import,都尽量加上模块名限定,这样代码不会报错,但是可读性会高很多。

    1. from tasks.TaskUtils import *
    2. from tasks.TiaoZhanDaTi import *
    3. # 不含__init__函数
    4. TaskUtils.get_task_btn(driver, '挑战答题').click()
    5. # 含__init__函数,直接调用
    6. TiaoZhanDaTi(driver, db).da_ti()

    python的初始化方法 __init__(self, ...) 就很类似java的构造函数。

    • 第12个坑:导入自定义模块

    python导入自定义模块,对比java其实就是导入一个类的事情,但在python里面,要复杂得多。我尝试下来,相对最简单,也最灵活的方式,先展示项目目录结构:

    +---py_project
    |   +---utils

    |       +---BasePage.py

    |   +---tasks

    |       +---TaskUtils.py

    TaskUtils.py需要引入BasePage.py,代码如下:

    1. import sys
    2. from os.path import join, dirname
    3. sys.path.append(join(dirname(__file__), '..'))
    4. from utils.BasePage import *
    • 第13个坑:python中的None与NULL

    None是一个对象,而NULL是一个类型。
    Python中没有NULL,只有None,None有自己的特殊类型NoneType。
    None不等于0、任何空字符串、False等。
    在Python中,None、False、0、""(空字符串)、[](空列表)、()(空元组)、{}(空字典)都相当于False。

    判断变量是否为空的高效方法是:
    if X is None
    if not X:当X为None、False、""、0、[]、()、{}时,not X为真,无法分辨
    if not X is None:等价于if not (X is None)、if X is not None
    ————————————————
    版权声明:本文为CSDN博主「小小的刀」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:python中的None与NULL_小小的刀的博客-CSDN博客_python的none和null

    • 第14个坑:Python 字符串前加f,r,u,b的含义

    f表示字符串内支持大括号内的python表达式,如:logger.info(f"Total time taken: {time.time() - start_time}");
    r表示去掉反斜杠的转移机制,如:logger.info(r"Test\n\n\n") 表示单纯字符串而不表示换行;
    u一般出现在中文字符串前,防止出现乱码;
    b表示这是一个bytes类型对象,在网络编程中,服务器和浏览器只认bytes类型数据,如:response=b'

    Hello World

    '

    ————————————————
    版权声明:本文为CSDN博主「小小的刀」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:Python 字符串前加f,r,u,b的含义_努力搬砖的刷刷碗的博客-CSDN博客_python字符串前加f

    • 第15个坑:swipe滑动操作

    我要的元素需要通过屏幕下滑才能找到,然后可能还不止滑一次,swipe其实有点不稳定,勉强够用。截取部分代码如下:

    1. """
    2. 查找任务按钮
    3. """
    4. def get_task_btn(driver, task):
    5. print("准备进行> %s <任务" % task)
    6. code = '//android.view.View[@text="' + task + '"]/../../android.view.View[@index=3]'
    7. i = 0
    8. ele = None
    9. while i < 3:
    10. try:
    11. ele = driver.find_element(AppiumBy.XPATH, code)
    12. except NoSuchElementException:
    13. i = i + 1
    14. print("当前屏幕未找到 %s 按钮,第 %d 次滑动屏幕" % (task, i))
    15. BasePage(driver).swipe_up(0.3)
    16. else:
    17. print(task + "==>" + ele.text)
    18. break
    19. return ele
    1. """获取x轴宽度"""
    2. @property
    3. def x(self):
    4. return self.get_window_size['width']
    5. """获取y轴高度"""
    6. @property
    7. def y(self):
    8. return self.get_window_size['height']
    9. """
    10. 手指向上滑动
    11. scale: 滑动距离所占比例
    12. duration: 滑动从起点到终点坐标所耗费的时间ms
    13. 起点: 0.5x, (1+scale)*y/2
    14. 终点: 0.5x, (1-scale)*y/2
    15. """
    16. def swipe_up(self, scale=0.8, duration=800):
    17. print('swipe_up')
    18. self.driver.swipe(self.x*0.5, self.y*(1+scale)/2, self.x*0.5, self.y*(1-scale)/2, duration)

    这种滑动操作可以封装到一个模块就行,参考博客:App自动化测试(五)之swipe滑动操作_DesireYang的博客-CSDN博客

    如果一个页面遇到多个滚动窗口,参考这个:Multiple scroll views - Appium

    By instance

    1. # first scrollView
    2. # FindElement
    3. MobileElement element = (MobileElement) driver.findElement(MobileBy.AndroidUIAutomator(
    4. "new UiScrollable(new UiSelector().scrollable(true).instance(0))" +
    5. ".scrollIntoView(new UiSelector().text(\"exact_text\"))"));
    6. # second scrollView
    7. # FindElement
    8. MobileElement element = (MobileElement) driver.findElement(MobileBy.AndroidUIAutomator(
    9. "new UiScrollable(new UiSelector().scrollable(true).instance(1))" +
    10. ".scrollIntoView(new UiSelector().text(\"exact_text\"))"));

    By id

    1. # FindElement
    2. MobileElement element = (MobileElement) driver.findElement(MobileBy.AndroidUIAutomator(
    3. "new UiScrollable(new UiSelector().resourceIdMatches(\".*part_id.*\").scrollable(true))" +
    4. ".scrollIntoView(new UiSelector().text(\"exact_text\"))"));
    • 最后一个不算是坑的坑:SQLite3获取新插入的记录ID及ROWID

    1. 创建表时,主键要创建为 INTEGER PRIMARY KEY,其实我觉得最好再加上自增 AUTOINCREMENT ,这样最简单最完美。

    2. 带有 INTEGER PRIMARY KEY 列的SQLite 数据库表,其 rowid 就是 该INTEGER PRIMARY KEY 列。所以,使用 cursor.lastrowid 得到就是我们的ID值了。

    注意:INTEGER PRIMARY KEY  不要写成了 int primary key。大小写无所谓,但是 integer 不要写成了 int,因为 int 不是 sqlite 的基本数据类型。
    ————————————————
    版权声明:本文为CSDN博主「qilei2010」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:【Python】SQLite3获取新插入的记录ID及ROWID探究_qilei2010的博客-CSDN博客_sqlite3 获取自增id

  • 相关阅读:
    DRF 过滤排序分页异常处理
    179基于matlab的2D-VMD处理图像
    LeetCode每日一题(1706. Where Will the Ball Fall)
    JAVA基础(秋招总结)
    【JVM】运行时数据区、程序计数器
    GIRAFFE学习笔记
    Python数字类型
    手机升级STM32单片机,pad下载程序,手机固件升级单片机,局域网程序下载,STM32单片机远程下载升级
    项目管理具体是做什么的?
    Scala 基础 (四):函数式编程【从基础到高阶应用】
  • 原文地址:https://blog.csdn.net/qingzhukl/article/details/125875724