Appium是一个开源的,适用于原生或混合移动应用( hybrid mobile apps )的自动化测试工具,Appium应用WebDriver: JSON wire protocol驱动安卓和iOS移动应用。它是继承自selenium的webdriver,所以它支持selenium的大部分API,且也有一些特殊的使用方式。
appium desktop是一款C/S架构的软件,其内集成了appium service。我们一般主要使用appium desktop来完成元素的定位及启动appium service。
使用appium desktop定位元素
连接真机时,需要使用文件传输模式,且在开发者选项中设置USB调试模式
是一组设置的键值对的集合,其中键对应设置的名称,而值对应设置的值,主要用于通知appium服务器建立需要的session。
Capability的使用方式可以参考官方文档:https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/caps.md
我们这里讲解几个常用的key_value。
platformName: 设置被测设备的系统,如:Android
platformVersion:被测设备系统的版本
deviceName:被测设备的序列号(使用adb devices查看)
appPackage:被测app的包名
appActivity:被测app需打开的页面的活动名
noReset:启动时是否不重置app,可以跳过登录,首次打开的弹出等(true或者false)
unicodeKeyboard:设置是否允许中文输入。(true或者false)
resetKeyboard:设置是否允许中文输入。(true或者false)
desired_caps:# 在首次运行测试用例时,不关闭app。默认在首次运行测试用例时会先关闭app,再打开
1.获取连接的设备信息:adb devices
如果设备未连接:adb connect 127.0.0.1:21503来手动连接。adb disconnect 127.0.0.1:21503手动断开连接
如果adb devices一直获取不到设备信息,可以将sdk下的adb.exe复制到模拟器目录下替换原来的adb.exe(需关闭所有adb进程)
如果还是adb连接不上设备,可以adb kill-server,然后再连接
2.获取app的包名:
方式一:如已安装对应的app:adb shell pm list packages -3(-3表示只看第三方的app)
方式二:如还未安装对应的app:aapt d badging 安装包的路径
3.获取app的活动名:
方式一:如已安装对应的app:adb shell dumpsys activity | findstr mFocusedActivity(获取正在运行的app的包名和活动名)
方式二:如还未安装对应的app:aapt d badging apk 安装包的路径
4.安装apk软件
adb install apk文件路径
第一步:导入webdriver
from appium import webdriver
第二步:设置Capability(被测设备及App的相关信息)
# 设置Capability(被测设备及App的相关信息)
desired_caps ={
}
desired_caps['platformName'] = 'Android' # 设置被测设备的系统
desired_caps['platformVersion'] = '7.1.2' # 被测设备系统的版本
desired_caps['deviceName'] = '127.0.0.1:21503 device' # 被测设备的序列号
desired_caps['appPackage'] = 'com.jingdong.app.mall' # 被测app的包名
desired_caps['appActivity'] = '.MainFrameActivity' # 被测app需打开的页面的活动名
desired_caps['noReset'] = 'true' # 启动时是否不重置app,可以跳过登录,首次打开的弹出等
desired_caps['unicodeKeyboard'] = 'true' # 设置中文输入
desired_caps['resetKeyboard'] = 'true' # 设置中文输入
desired_caps['desired_caps'] = 'true' # 在首次运行测试用例时,不关闭app。默认在首次运行测试用例时会先关闭app,再打开
第三步:创建一个driver
格式:webdriver.Remote(‘http://appium server的ip:端口号/wd/hub’,Capability信息)
from appium import webdriver
# 设置Capability(被测设备及App的相关信息)
desired_caps ={
}
desired_caps['platformName'] = 'Android' # 设置被测设备的系统
desired_caps['platformVersion'] = '7.1.2' # 被测设备系统的版本
desired_caps['deviceName'] = '127.0.0.1:21503 device' # 被测设备的序列号
desired_caps['appPackage'] = 'com.jingdong.app.mall' # 被测app的包名
desired_caps['appActivity'] = '.MainFrameActivity' # 被测app需打开的页面的活动名
desired_caps['noReset'] = 'true' # 启动时是否不重置app,可以跳过登录,首次打开的弹出等
desired_caps['unicodeKeyboard'] = 'true' # 设置中文输入
desired_caps['resetKeyboard'] = 'true' # 设置中文输入
# 生成对应的driver
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub',desired_caps)
(1)ID定位:(本质是找app页面元素的resource-id属性的值),当页面有多个相同id的元素时,就不能使用id定位了。
方式一:driver.find_element_by_id('com.xueqiu.android:id/home_search')
方式二:driver.find_element(MobileBy.ID,'com.xueqiu.android:id/home_search')
(2)class name 定位(不推荐,一般定位不准确)
driver.find_element_by_class_name('class值')
driver.find_element(MobileBy.CLASS_NAME,'class值')
(3)xpath定位(使用相对xpath,绝对xpath太长会定位失败)
driver.find_element_by_xpath('//*[@resource-id="com.xueqiu.android:id/name" and @text="阿里巴巴"]')
driver.find_element(MobileBy.XPATH,'//*[@resource-id="com.xueqiu.android:id/name" and @text="阿里巴巴"]')
driver.find_element(MobileBy.XPATH,'//*[contains(@text,"次外出")]').click()
Appium中定位元素的方式与selenium十分相似,可以参考selenium的使用。
还包括一些Android和ISO特有的一些定位方式,需要时自己去查看
uiautomatorviewer是sdk自带的一个工具,也可以定位元素。
这个工具在我们的sdk目录的tools目录下,运行该文件即可打开uiautomatorviewer工具。uiautomatorviewer和appiunm的定位工具不能同时打开。
appium中元素的常用方法与selenium也十分相似,所以我们可以参考selenium中的方法。
import pytest
from appium import webdriver
from selenium.webdriver.common.by import By
class TestXueQiu:
def setup(self):
# 设置Capability(被测设备及App的相关信息)
desired_caps = {
}
desired_caps['platformName'] = 'Android' # 设置被测设备的系统
desired_caps['platformVersion'] = '5.1.1' # 被测设备系统的版本
desired_caps['deviceName'] = '127.0.0.1:21503 device' # 被测设备的序列号
desired_caps['appPackage'] = 'com.xueqiu.android' # 被测app的包名
desired_caps['appActivity'] = '.main.view.MainActivity' # 被测app需打开的页面的活动名
desired_caps['noReset'] = 'true' # 每次打开被测app时,不清除之前的数据
desired_caps['unicodeKeyboard'] = 'true' # 重置键盘,如果需要输入中文需要设置这一项
desired_caps['resetKeyboard'] = 'true' # 允许重置键盘,如果需要输入中文需要设置这一项
# 生成对应的driver
self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
# 设置隐私等待
self.driver.implicitly_wait(5)
def teardown(self):
self.driver.quit()
def test_xueqiu_002(self):
"""
1.打开雪球app
2.定位首页搜索框
3.判断搜索框是否可用
4.打印搜索框的name值
5.点击搜索框输入’alibaba‘
6.判断阿里巴巴是否可见
7.如果可见打印“搜索成功”,否则打印“搜索失败”
:return:
"""
ele_search = self.driver.find_element_by_id('com.xueqiu.android:id/home_search')
ele_search_enable = ele_search.is_enabled()
print('\n搜索框是否可用:',ele_search_enable)
ele_search_name = ele_search.get_attribute('name')
print('搜索框的name:',ele_search_name)
print('搜索框的坐标:',ele_search.location)
print('搜索框的长宽:',ele_search.size)
if ele_search_enable:
ele_search.click()
self.driver.find_element_by_id('com.xueqiu.android:id/search_input_text').send_keys('alibaba')
ele_ali = self.driver.find_element_by_xpath('//*[@resource-id="com.xueqiu.android:id/name" and @text="阿里巴巴"]')
ele_ali_displayed = ele_ali.is_displayed()
# print('阿里巴巴是否可见:',ele_ali_displayed)
if ele_ali_displayed:
print('搜索成功')
else:
print('搜索失败')
appium中的等待跟selenium中一样。这里不再讲解
我们可以使用TouchAction来完成对手机触碰的操作
格式:
action = TouchAction(self.driver) # 实例化一个touchaction对象
action.方法 # 给action添加方法
action.perform() # 执行action
Touchaction的常用方法:
1.按下:
action.press(ele)----------在对应元素的位置按下,参数为某个元素
action.press(x=a,y=b)----------在(a,b)坐标的位置按下,参数为x,y
2.释放:
action.release()-----释放操作
3.移动:
action.move_to(ele)----------移动到对应元素的位置,参数为某个元素
action.move_to(x=a,y=b)----------移动到(a,b)坐标的位置,参数为某个元素
4.长按
action.long_press(ele)-------在对应元素的位置长按,参数为某个元素
action.long_press(x=a,y=b)-------在(a,b)坐标的位置长按,参数为x,y
5.等待
action.wait(200)------等待200毫秒
6.点击
action.tap(ele)----------点击对应元素的位置,参数为某个元素
action.tap(x=a,y=b)----------点击(a,b)坐标的位置,参数为某个元素
方式一:action.press(ele1).move_to(ele2).release()---------从元素ele1滑动到元素ele2
方式二:action.press(x=70,y=1160).move_to(x=70,y=610).release()-----从(70,1160)滑动到(70,610)
注意,方式二的定位跟手机屏幕大小有关,兼容性不太好,可以进行改进,详见方式3
方式三:
win_size= self.driver.get_window_size() #获取设备屏幕的大小
width = win_size['width'] #屏幕的宽
height = win_size['height'] #屏幕的长
x = int(width/2)
y1 = int(height * 4/5)
y2 = int(height * 1/5)
action = TouchAction(self.driver)
action.press(x=x,y=y1).move_to(x=x,y=y2).release() #实现滑动,而且清楚了屏幕大小对坐标点的影响
action.perform()
举例:
//*[@resource-id="com.xueqiu.android:id/name" and @text="阿里巴巴"]--------定位resource-id="com.xueqiu.android:id/name和text="阿里巴巴"的任何元素
//*[@text='BABA']/../../../..//*[@resource-id='com.xueqiu.android:id/current_price']----定位text='BABA'的任意元素的父节点的父节点的父节点的父节点的子孙节点中resource-id='com.xueqiu.android:id/current_price的任意类型的元素
举例:
1.driver.find_element_by_android_uiautomator('new UiSelector().resourceId("com.xueqiu.android:id/login_account")')--使用resourceId定位
2.driver.find_element_by_android_uiautomator('new UiSelector().textContains("帐号密码登录")')---使用text模糊匹配定位
3.driver.find_element_by_android_uiautomator('new UiSelector().resourceId("com.xueqiu.android:id/tab_name").text("我的")')----使用resourceId和text组合定位
# uiautomator实现滚动查找页面元素
self.driver.find_element_by_android_uiautomator('new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text("持有封基").instance(0));').click()
----滚动查找页面text为‘持有封基’的元素
self.driver.make_gsm_call('13118172284',GsmCallActions.CALL)
# 第一个参数:来电号码
# 第二个参数:行为(GsmCallActions.CALL--表示来电话)
self.driver.send_sms('131-181-72284','hello.sb')
# 第一个参数:号码
# 第二个参数:发送的信息
self.driver.set_network_connection(1)
# 参数:需要设置的网络等级
# 0:数据模式关闭,wifi模式关闭,飞行模式关闭
# 1:数据模式关闭,wifi模式关闭,飞行模式开启
# 2:数据模式关闭,wifi模式开启,飞行模式关闭
# 4:数据模式开启,wifi模式关闭,飞行模式关闭
# 6:数据模式开启,wifi模式开启,飞行模式关闭
self.driver.get_screenshot_as_file('./kkk.png')
格式:element.get_attribute(‘属性名’)
element1.get_attribute('text')--------获取元素element1的text属性的值
常用的属性:
text----文本
resource-id----id属性
class----class属性
content-desc
checkable----是否可检测
checked----是否已检测
enable----是否可操作
fousable----是否可聚焦
focused----是否已聚焦
scrollable----是否可滚动
long-clickable----是否可长按
selected----是否已选择
bounds----坐标值
格式:assert 条件
条件为真,用例通过,条件为假,用例失败
官网:https://github.com/hamcrest/PyHamcrest
使用方法:
安装:pip install PyHamcrest
导入:from hamcrest import *
运用:assert_that(要断言的对象,断言方法,‘失败提醒信息’)-----失败提醒信息参数可以不加
# 常用的断言匹配方法
#对象的断言方法:
assert_that('ass',equal_to('ass'))---匹配相等对象
assert_that('shj',has_length(3))----匹配对象的长度是否满足
assert_that(element1,has_property('text'))---匹配元素element1有text属性
assert_that(element1,has_property('text','我的'))--匹配元素element1有text属性,且属性值为‘我的’
assert_that(6,close_to(8,2))---匹配接近给定的数字值
这里我们只介绍了几种方法,更多的可以自己去官网查看
driver.page_source
appium支持原生,web,混合等多种架构app的测试。
1.获取浏览器驱动。chromedriver
第一步:查看浏览器版本
第一种方式:使用详情查看,这种有可能看不到
第二种方式:使用真机或模拟器中的浏览器,访问https://liulanmi.com/labs/core.html查看
2.下载对应的driver
国内镜像:https://npm.taobao.org/mirrors/chromedriver/
github:https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/web/chromedriver.md
3.脚本编写driver配置
caps = {
}
caps['platformName'] = 'Android' # 被测设备为Android
caps['platformVersion'] = '5.1.1' # 被测设备系统版本
caps['deviceName'] = '127.0.0.1:21503' # 被测设备
caps['browserName'] = 'Browser' # 设置使用默认浏览器
caps['chromedriverExecutable'] = 'E:/driver/app_driver/chromedriver.exe' # 设置chromedriver的路径
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub',caps)
webview测试,不像原生app。不能使用appium的inspector和uiautomator来定位元素
方法:在PC的chrome浏览器上,使用:chrome://inspect,来渲染手机端的浏览器。实现定位
这样要求手机端必须能访问到google网址,需要。可以网上下载离线开发调试工具包
解决办法:https://www.cnblogs.com/slmk/p/7591126.html
定位方法:跟web页面一样
原生页面元素使用app的定位方式,webview页面的元素使用chrome://inspect定位
测试混合应用app,出了需要设置apppackage和appactivity外,还需要设置chromedrier路径。
获取chromedrier版本的方法:
第一步:获取app中webview的包名
第二步:通过包名获取版本信息
newCommandTimeout设置appium等待下一个请求发送的间隔时间,默认是60s
在自动化用例中,存在上传下载大文件时,可以设置newCommandTimeout长一些
caps['newCommandTimeout'] = 300
# 设置appium等待时间为300s
udid设置自动化要使用的设备,用于多设备的情况。默认是选择设备列表中的第一个设备
caps['udid'] = 'asiodf'
# 选择设备asiodf
实际上deviceName并不能决定我们选择的设备。udid才可以
autoGrantPermissions用于处理权限授予的弹框,值为false或者true
注意:如果noReset为true,autoGrantPermissions则不生效
noReset表示在建立session时,是否不关闭app,清除app数据,卸载app。值为true或者false
fullReset与noReset相反,在建立session时,是否关闭app,清除app数据,并且卸载app。值为true或者false
dontStopAppOnReset:在脚本开始运行时,不关闭app。默认为false,会关闭app
本节重点:理解客户端,appium server,uiautomator2-server的协议
appium客户端主要使用webdriver和Mobile JSON Wire Protocol协议(底层还是HTTP),支持多语言。
webdriver协议是w3c协议的一种,定义了操作web端浏览器的各种接口和协议规范。
我们使用appium或者selenium编写脚本,实际上就是使用webdriver协议。来实现自动化用例
Mobile JSON Wire Protocol协议是appium开发团队,在webdriver协议之上封装的一套协议,用于解决对移动端的一些操作支持。Mobile JSON Wire Protocol协议也是w3c组织的一员。
appium客户端使用两大协议将自动化的请求发送到appium server,appium server再遵循特定的协议将请求发送给uiautomator服务端,由uiautomator服务端来实际完成各种自动化操作
#! /usr/bin/python
# -*- coding: utf-8 -*-
import time
import pytest
from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
class TestAutoClock:
def setup(self):
caps = {
}
caps['platformName'] = 'Android' # 被测设备为Android
caps['platformVersion'] = '5.1.1' # 被测设备系统版本
caps['deviceName'] = '127.0.0.1:21503' # 被测设备
caps['appActivity'] = '.launch.WwMainActivity' # 被测app要打开的页面
caps['appPackage'] = 'com.tencent.wework' # 被测app的包名
caps['noReset'] = 'true' # 启动时是否不重置app,可以跳过登录等
caps['unicodeKeyboard'] = 'true' # 设置中文输入
caps['resetKeyboard'] = 'true' # 设置中文输入
caps['settings[waitForIdleTimeout]'] = 0
self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub',caps)
self.driver.implicitly_wait(15)
def teardown(self):
self.driver.quit()
def test_autoclock(self):
"""
企业微信自动打卡
:return:
"""
self.driver.find_element(MobileBy.XPATH,'//*[@text="工作台"]').click()
# self.driver.find_element_by_android_uiautomator('new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text("").instance(0));').click()
# 滚动查找
self.driver.find_element(MobileBy.ANDROID_UIAUTOMATOR,
'new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text("打卡").instance(0));').click()
self.driver.find_element(MobileBy.XPATH,'//*[@text="外出打卡"]').click()
# 查找text包含xx的元素
self.driver.find_element(MobileBy.XPATH,'//*[contains(@text,"次外出")]').click()
time.sleep(2)
assert '外出打卡成功' in self.driver.page_source
if __name__ == "__main__":
pytest.main(['-vs','test_wework_autoclock.py'])