目录
Appium学习
Appium简介
Appium特性
- 开源的移动端的自动化测试框架;
- 可测试原生的、混合的、以及移动端的Web项目;
- 可测试Android/IOS/Firefox OS;
- 支持跨平台;
Appium理念
- 无需为自动化测试重编译或修改app;
- 不限定某种语言或框架;
- 无需重写api,改进已有的即可;
- 移动端自动化测试应该是开源的;
Appium设计思想
- 使用client-server的设计模式;(实现了理念2)
- 扩展了webdriver的协议
Appium基本概念
Client-server设计模式
Appium核心是一个暴露了一系列Rest Api的server。
流程:监听一个端口==》接受由client端发送过来的命令==》翻译这些命令并把这些命令转换成移动设备可以理解的形式发送给移动设备==》接受移动设备执行完命令的结果==》把执行结果返回给client端
在这里client端其实就是发送命令的设备,也就是执行代码的设备,这也就不限制是哪种代码(可以挑你喜欢的),只要它实现webdriver标准协议。
优点:
- 支持多语言;
- server可以放在任意机器上,包括云服务器;
Session
Session,会话。在webdriver/Appium中,所有工作都是在session start之后才能进行的。
一般来说,通过post/session这个URL,然后传入Desired Capabilities就可以开启session。
开启session后,会返回一个全局唯一的session id,之后所有的请求都必须带上这个session id。Session id代表了你所打开的浏览器或者是移动设备的模拟器。
Session Id的全局唯一,让同一台机器上启动多个session变成了可能。
Desired Capabilities
携带了一系列的配置信息,类似于Pythond的字典,传输时是Json对象。
最主要的作用是告诉server本次测试的上下文(环境)。
Appium Server
在命令行用appium命令打开的东西。
启动命令: apppium -a 127.0.0.1 -p 4723 --session-override
Tips:--no-reset 即可避免执行用例的时候再次安装app,
--session-override 不必每次重启session
成功启动截图:
若启动失败,命令行输入 adb devices查看当前连接设备
Appium Clients
对原生的webdriver进行的一些移动端的扩展,加入了一些方便的方法。
Appium.app/.exe
Appium Server的GUI版本。
Appium Capability参数配置
以下
公用部分
Android特有
IOS特有
配置优化
将代码中写死的配置信息抽离,存放在yaml配置文件,使用
对象数据类型来存储数据;然后调用load()方法读取数据,从而实现数据和代码的分离。
desired_caps.yaml
platformName: Android
platformVersion: 5.1.1
deviceName: 127.0.0.1:62025
app: C:\Users\[Your UserName]\Desktop\Appium software\kaoyan3.1.0.apk
noReset: False
appPackage: com.tal.kaoyan
appActivity: com.tal.kaoyan.ui.activity.SplashActivity
ip: 127.0.0.1
port: 4723
capability_yaml.py
from appium import webdriver
import yaml
file=open('desired_caps.yaml','r')
data=yaml.load(file)
desired_caps={}
desired_caps['platformName']=data['platformName']
desired_caps['platformVersion']=data['platformVersion']
desired_caps['deviceName']=data['deviceName']
desired_caps['app']=data['app']
desired_caps['noReset']=data['noReset']
desired_caps['appPackage']=data['appPackage']
desired_caps['appActivity']=data['appActivity']
driver = webdriver.Remote('http://'+str(data['ip'])+':'+str(data['port'])+'/wd/hub', desired_caps)
Tips:"http://127.0.0.1:4723/wd/hub"的解释:
(1)127.0.0.1是环回(loopback)地址,用来测试本机的TCP/IP协议栈;
(2)端口号:4723
这个值不是固定的,唯一的要求就是——不要冲突,你可以在cmd你查看目前占用的端口号,输入命令
netstat -ano查看所有端口占用;
netstat -ano | findstr “4723” 查看4723端口号被哪个程序占用
也可以打开你的appium,在General setting-Server-port中查看。
在这里就可以修改端口号了,但是你在这里怎么改端口号,那么你代码里就要对应的修改端口号。
(3)/wd/hub: wd 可以理解是WebDriver 的缩写
hub 是指主(中心) 节点,在selenium 分布式里中心节点
Appium元素定位
元素定位方式:
- ID
- Name
- className
- 相对定位
- Xpath定位
- List定位
- H5页面元素定位
- Uiautomator定位
ID定位
find_element_by_id()
方法探索:
通过捕捉NoSuchElementException
异常判断元素是否存在
try:
code
except NoSuchElementException:
code
else:
code
Name定位
find_element_by_name()
该方法稳定性不好
className定位
find_element_by_class_name()
相对定位
先找到该元素有对应属性的父节点,然后基于父节点进行元素定位
Xpath定位
通常使用xpath相对路径和属性定位。
-
Xpath路径表达式:
-
Xpath匹配符
List定位
相同className属性值元素无法区分定位,这里需要使用List定位来解决问题。List定位首先是通过find_elements_by_XX
获取一组相同的class属性的元素,然后使用数组下标来区分标记不同元素进行相关操作。
Uiautomator定位
find_element_by_android_uiautomator()
定位方法
ID定位
find_element_by_android_uiautomator('new UiSelector().resourceId(value)')
text定位
find_element_by_android_uiautomator('new UiSelector().text(value)')
className定位
find_element_by_android_uiautomator('new UiSelector().className(value)')
Appium元素等待
强制等待
import time
time.sleep(1)
隐式等待
针对全部元素设置的等待时间
driver.implicitly_wait(1)
显式等待
针对某个元素设置的等待时间
方法WebDriverWait格式参数如下:
from selenium.webdriver.support.ui import WebDriverWait
WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)
driver : WebDriver
timeout : 最长超时时间,默认以秒为单位
poll_frequency : 休眠时间的间隔时间,默认为0.5秒
ignored_exceptions : 超时后的异常信息,默认情况下抛NoSuchElementException异常
WebDriverWait()一般和until()或until_not()方法配合使用,另外,lambda提供了一个运行时动态创建函数的方法。
from selenium.webdriver.support.ui import WebDriverWait
WebDriverWait(driver,10).until(lambda x:x.find_element_by_id("elementID"))
Toast元素识别
Toast是一种简易的消息提示框。
Appium 1.6.3开始支持识别Toast内容,主要是基于UiAutomator2,因此需要在Capablity配置如下参数:
desired_caps['automationName']='uiautomator2'
安装appium-uiautomator2-driver:
cnpm install appium-uiautomator2-driver
安装selenium模块:
pip install selenium
注意:Toast内容为中文时,顶部必须注释# coding=utf-8 否则会因为编解码导致文字识别失败。
Appium截图
1.直接保存当前屏幕截图到当前脚本所在位置
driver.save_screenshot('Png.png')
2.将截图保存到指定位置
driver.get_screenshot_as_file('./images/login.png')
Appium滑动操作
在Appium中模拟用户滑动操作需要使用swipe方法,该方法定义如下:
def swipe(self, start_x, start_y, end_x, end_y, duration=None):
"""Swipe from one point to another point, for an optional duration.
:Args:
- start_x - x-coordinate at which to start
- start_y - y-coordinate at which to start
- end_x - x-coordinate at which to stop
- end_y - y-coordinate at which to stop
- duration - (optional) time to take the swipe, in ms.
:Usage:
driver.swipe(100, 100, 100, 400)
Appum连续滑动操作
Touch Action包含一些列操作,比如按压、长按、点击、移动、暂停。由着些不同操作可以组成一套动作。使用TochAction需要先导入对应的模块from appium.webdriver.common.touch_action import TouchAction
按压
方法:press()
开始按压一个元素或坐标点(x,y),通过手指按压手机屏幕某个位置。
press也可以接收屏幕的坐标(x,y)。
press(self, el=None, x=None, y=None)
TouchAction(driver).press(x=0,y=308)
长按压
方法:longPress()
开始按压一个元素或坐标点(x,y)。 相比press()方法,longPress()多了一个入参,既然长按,得有按的时间吧。duration以毫秒为单位。1000表示按一秒钟。其用法与press()方法相同。
long_press(self, el=None, x=None, y=None, duration=1000)
点击
方法:tap()
对一个元素或控件执行点击操作。用法参考press()
。
tap(self, element=None, x=None, y=None, count=1)
移动
方法:move_to()
将指针从上一个点移动到指定的元素或点。
move_to(self, el=None, x=None, y=None)
注意:移动到目位置有时是算绝对坐标点,有时是基于前面一个坐标点的偏移量,这个要结合具体App来实践。
暂停
方法:Wait()
暂停脚本的执行,单位为毫秒。
wait(self, ms=0)
释放
方法:release()
结束的行动取消屏幕上的指针。
release(self)
执行
方法:perform()
执行的操作发送到服务器的命令操作。
perform(self)
组和用法
TouchAction(driver).press(x=243,y=381).wait(2000)
.move_to(x=455,y=390).wait(1000)
.move_to(x=643,y=584).wait(1000)
.move_to(x=647,y=784).wait(1000)
.release().perform()
Appium多点触控操作
MultiAction 是多点触控的类,可以模拟用户多点操作。主要包含 add()
和 perform()
两个方法,
MultiAction可以结合前面所学的 ActionTouch可以模拟出用户的多个手指滑动的操作效果;
from appium.webdriver.common.multi_action import MultiAction
from appium.webdriver.common.touch_action import TouchAction
# add(self, *touch_actions)将TouchAction对象添加到MultiAction中,稍后再执行。
# touch_actions - 一个或多个TouchAction对象,描述一个手指要执行的动作链
a1 = TouchAction(driver).press(el1).move_to(el2).release()
a2 = TouchAction(driver).press(el2).move_to(el1).release()
MultiAction(driver).add(a1, a2)
# perform(self) 执行存储在对象中的操作
a1 = TouchAction(driver).press(el1).move_to(el2).release()
a2 = TouchAction(driver).press(el2).move_to(el1).release()
MultiAction(driver).add(a1, a2).perform()
Appium日志收集
日志格式配置
保存为 log.cof
[loggers]
keys=root,infoLogger
[logger_root]
level=DEBUG
handlers=consoleHandler,fileHandler
[logger_infoLogger]
handlers=consoleHandler,fileHandler
qualname=infoLogger
propagate=0
[handlers]
keys=consoleHandler,fileHandler
[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=form02
args=(sys.stdout,)
[handler_fileHandler]
class=FileHandler
level=INFO
formatter=form01
args=('runlog.log', 'a')
[formatters]
keys=form01,form02
[formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
[formatter_form02]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
在需要调用的模块增加如下代码:
import logging
import logging.config
CON_LOG='log.conf'
logging.config.fileConfig(CON_LOG)
logging=logging.getLogger()
具体调用:
logging.info('This is log info')
Appium模拟器按键操作
按键方法
KEYCODE_CALL 拨号键 5
KEYCODE_ENDCALL 挂机键 6
KEYCODE_HOME 按键Home 3
KEYCODE_MENU 菜单键 82
KEYCODE_BACK 返回键 4
KEYCODE_SEARCH 搜索键 84
KEYCODE_CAMERA 拍照键 27
KEYCODE_FOCUS 拍照对焦键 80
KEYCODE_POWER 电源键 26
KEYCODE_NOTIFICATION 通知键 83
KEYCODE_MUTE 话筒静音键 91
KEYCODE_VOLUME_MUTE 扬声器静音键 164
KEYCODE_VOLUME_UP 音量增加键 24
KEYCODE_VOLUME_DOWN 音量减小键 25
Tips:后面的数字为 keycode
使用方法
driver.press_keycode(4) #发送keycode,功能:按键
driver.keyevent(4) #发送keycode,功能:按键,与press_keycode无区别
driver.hide_keyboard() #iOS使用key_name隐藏,安卓不使用参数,功能:隐藏键盘
driver.long_press_keycode(4) #发送keycode,功能:长按键