学习appium必备的避坑指南

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

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

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

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

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

JAVA_HOME    C:\Program Files\Java\jdk1.8.0_211
CLASSPATH    .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;
ANDROID_HOME    D:\Programs\androidsdk
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输入命令:

方法一
adb shell dumpsys activity recents | find "intent={"

方法二
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找不到驱动程序。

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

学习appium必备的避坑指南_第1张图片

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

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

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

学习appium必备的避坑指南_第2张图片

  •  第六个坑: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,都尽量加上模块名限定,这样代码不会报错,但是可读性会高很多。

from tasks.TaskUtils import *
from tasks.TiaoZhanDaTi import *

# 不含__init__函数
TaskUtils.get_task_btn(driver, '挑战答题').click()
# 含__init__函数,直接调用
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,代码如下:

import sys
from os.path import join, dirname
sys.path.append(join(dirname(__file__), '..'))
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其实有点不稳定,勉强够用。截取部分代码如下:

"""
查找任务按钮
"""
def get_task_btn(driver, task):
	print("准备进行> %s <任务" % task)
	code = '//android.view.View[@text="' + task + '"]/../../android.view.View[@index=3]'
	i = 0
	ele = None
	while i < 3:
		try:
			ele = driver.find_element(AppiumBy.XPATH, code)
		except NoSuchElementException:
			i = i + 1
			print("当前屏幕未找到 %s 按钮,第 %d 次滑动屏幕" % (task, i))
			BasePage(driver).swipe_up(0.3)
		else:
			print(task + "==>" + ele.text)
			break
	return ele
"""获取x轴宽度"""
@property
def x(self):
	return self.get_window_size['width']

"""获取y轴高度"""
@property
def y(self):
	return self.get_window_size['height']

"""
手指向上滑动
scale: 滑动距离所占比例
duration: 滑动从起点到终点坐标所耗费的时间ms
起点: 0.5x, (1+scale)*y/2
终点: 0.5x, (1-scale)*y/2
"""
def swipe_up(self, scale=0.8, duration=800):
	print('swipe_up')
	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

# first scrollView
# FindElement
MobileElement element = (MobileElement) driver.findElement(MobileBy.AndroidUIAutomator(
        "new UiScrollable(new UiSelector().scrollable(true).instance(0))" +
         ".scrollIntoView(new UiSelector().text(\"exact_text\"))"));

# second scrollView
# FindElement
MobileElement element = (MobileElement) driver.findElement(MobileBy.AndroidUIAutomator(
        "new UiScrollable(new UiSelector().scrollable(true).instance(1))" +
         ".scrollIntoView(new UiSelector().text(\"exact_text\"))"));

By id

# FindElement
MobileElement element = (MobileElement) driver.findElement(MobileBy.AndroidUIAutomator(
        "new UiScrollable(new UiSelector().resourceIdMatches(\".*part_id.*\").scrollable(true))" +
         ".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

你可能感兴趣的:(自动化测试,学习,python,appium)