Menu:获取属性与断言
分析getattribute源码,可以通过github下载appium源码(appium-uiautomator2-server),因为源码是java项目通过andriod studio打开
#通常我们不需要通过下载源码分析,可以通过uiautomatorviewer直接查看元素的属性值
ps -ef |grep appium #查看appium进程
kill 31232 #杀死进程id为31232的程序
#get_attribute 查看元素的属性值
print(self.driver.find_element(By.XPATH, '//*[@text="我的"]').get_attribute("content-desc"))
print(self.driver.find_element(By.XPATH, '//*[@text="我的"]').get_attribute("enabled"))
print(self.driver.find_element(By.XPATH, '//*[@text="我的"]').get_attribute("clickable"))
hamrest断言
pip install pyhamcrest #安装hamrest
#使用
#需要使用assert_that()方法
def test_hamcrest(self):
#实际值 期望值
hamcrest.assert_that(10, hamcrest.equal_to(10),"这是一个提示")
#断言相近值比较
#实际值10 期望值close_to(10,2)为8-12
hamcrest.assert_that(10,hamcrest.close_to(10,2))
#断言字符串包含 contains_string()方法
hamcrest.assert_that("good job",hamcrest.contains_string("job"))
Menu:参数化用例
参数化
一些小细节
参数化要解决的是一个用例可以复用的问题,比如一个用例重复使用不同的数据,就可以使用参数化,比如同一个用例,有搜索股价,比较股价,都是同一个方法,只是数据不太一样
@pytest.mark.parametrize(‘searchkey,type,price’,[
(‘alibaba’,‘BABA’,180),
(‘xiaomi’,‘01810’,10)
用上面的方法去使用参数化
def test_search(self,searchkey,type,price) 函数的参数要和参数化的参数的数量一样,字符串也要一样
一个用例,有2组参数化,就会运行两次setup和teardown的方法
使用self.driver.find_element(By.ID,“com.xueqiu.android:id/search_input_text”).send_keys(f"{searchkey}"),使用f"{searchkey}"是一个好东西,可以搭配参数化使用
from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy as By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
import pytest
class TestFind():
#设置caps的值
def setup(self):
self.desire_cap= {
#默认是Android
"platformName":"android",
#adb devices的sn名称
"deviceName":"127.0.0.1:7555",
#包名
"appPackage":"com.xueqiu.android",
#activity名字
"appActivity":".view.WelcomeActivityAlias",
"noReset":"true",
"unicodeKeyboard":True
}
#运行appium,前提是要打开appium server
self.driver=webdriver.Remote("http://127.0.0.1:4723/wd/hub",self.desire_cap)
self.driver.implicitly_wait(5)
#这个加不加都行,因为参数化运行都会有setup的,setup就是启动app的过程了,这个就显得有点多余了
#但好像setup并不会初始化整个app,还会停留在前一个页面上,所以还是加上比较好
def teardown(self):
#导入的是MobileBy 使用通过取别名By来进行使用 'from appium.webdriver.common.mobileby import MobileBy as By'
self.driver.find_element(By.XPATH,'//*[@text="取消"]').click()
#这是参数化的函数,第一部分是参数化的名字,得和下面的函数参数一模一样,用字符串包含进去
#列表里面的元祖接受具体的参数化的数据,用逗号隔开,和list一样
@pytest.mark.parametrize('searchkey,type,price',[
('alibaba','BABA',180),
('xiaomi','01810',10)
])
#参数哈的函数的参数要和上面的参数名字保持一致
def test_search(self,searchkey,type,price):
"""
1.打开雪球app
2.点击搜索输入框
3.向搜索输入框输入“阿里巴巴”
4.在搜索的结果里选择阿里巴巴,然后点击
5.获取这只香港 阿里巴巴的股价,并判断这只股价的价格 > 自己设定的价格
6.通过参数化的方法,用一个用例判断阿里巴巴和小米的股价
:return:
"""
#显示等待进入主页,等主页的元素都加载好了
WebDriverWait(self.driver, 15).until(expected_conditions.element_to_be_clickable((By.XPATH,'//*[@text="我的"]')))
#点击搜索框
self.driver.find_element(By.ID,"com.xueqiu.android:id/tv_search").click()
#向搜索框输入阿里巴巴,小米等参数化的东西f"{searchkey}"是一个好用的东西
self.driver.find_element(By.ID,"com.xueqiu.android:id/search_input_text").send_keys(f"{searchkey}")
#找到搜索框预览结果的阿里巴巴,并点击
self.driver.find_element(By.XPATH,f"//*[@text='{type}']").click()
#选择HK股价的元素,这里是通过父类的方法去定位的
current_price=self.driver.find_element(By.XPATH,f"//*[@text='{type}']/../../..//*[@resource-id='com.xueqiu.android:id/current_price']")
#提取股价的text属性
current_price=float(current_price.text)
#判断股价是否大于自己设定的价格
assert current_price > price
Menu:android webview测试
webview
纯webview测试(只测试浏览器)的环境准备
手机端
被测浏览器:(不可以是第三方浏览器,比如UC浏览器、QQ浏览器)safari for ios and chrome,chromium,or browser for Android
PC端
1.安装chrome浏览器或者chromium,必须FQ,这样才能访问chrome://inspect/#devices 来进行元素定位
2.下载对应手机浏览器对应的driver
客户端代码:
#纯webview测试,desire_cap设置不使用"appPackage"、"appActivity",而使用
desire_cap设置
"browserName":"Browser" 或者 "browserName":"Chrome" 这个是指定的chrome浏览器
"chromedriverExecutable":r"c:\chrome\chromedriver.exe" 这个是指定的chromedriver的路径
如何查找app(browser)的版本:adb shell pm dump com.android.browser | findstr version
#adb shell pm list package 查看安卓手机上所有安装包
#adb shell pm list package | grep browser 获取browser包名为com.android.browser
#adb shell pm dump com.android.browser| grep version 获取浏览器版本
#adb shell pm dump com.android.chrome | grep version 获取chrome浏览器版本
adb shell pm dump com.android.webview | grep version 获取webview版本信息,下载对应的chromedriver版本即可,chromedriver版本向上兼容
#adb shell pm list package 查看安卓手机上所有安装包
#adb shell pm list package | grep webview 获取webview包名为com.android.webview
#adb shell pm dump com.android.webview | grep version 获取版本信息
纯webview测试(只测试浏览器)元素定位:
chrome://inspect/#devices 来进行元素定位,注意这时候使用的是selenium的By 'from selenium.webdriver.common.by import By'
案例:打开mumu自带的浏览器,访问百度
步骤:
不通过包来打开浏览器
访问百度
输入tongtong,并点击搜索
注:
adb shell pm dump com.android.webview | grep version 获取webview版本信息,下载对应的chromedriver版本即可,chromedriver版本向上兼容
#运行appium版本时,可以看到浏览器的chromedriver的版本,一般会到默认路径里面去找浏览器的chromedriver,如果客户端代码配置了"chromedriverExecutable",则到配置路径里面查找浏览器的chromedriver
第一次运行appium,看后台的路径可以找到浏览器的chromedriver的版本,还可以找到 chromedriver的路径
#支持的Chrome版本是向上兼容的,一般使用对应版本低一个版本即可
https://blog.csdn.net/huilan_same/article/details/51896672 这个网站的chromedriver和chrome版本的关系更加全
from time import sleep
from appium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
class TestFind():
def setup(self):
self.desire_cap= {
"platformName":"android",
"platformVersion":"6.0",
"deviceName":"127.0.0.1:7555",
#想要使用原生的浏览器就选择,Browser。想要选择chrome浏览器就输入Chrome
"browserName":"Browser",
"noRest":True,
#这里是指定chromedriver的路径,记得路径要全到包括chromedriver.exe
#adb shell pm dump com.android.webview | grep version 获取webview版本信息,下载对应的chromedriver版本即可,chromedriver版本向上兼容
"chromedriverExecutable":r"c:\chrome\chromedriver.exe"
}
self.driver=webdriver.Remote("http://127.0.0.1:4723/wd/hub",self.desire_cap)
self.driver.implicitly_wait(5)
def teardown(self):
self.driver.quit()
def test_browser(self):
#打开移动端的百度浏览器
self.driver.get("http://m.baidu.com")
#显示等待找到搜索框是否可见,expected_conditions里面传的locator必须是一个元祖
WebDriverWait(self.driver,10).until(expected_conditions.visibility_of_element_located((By.CSS_SELECTOR,"#index-kw")))
#搜索框输入tongtong
self.driver.find_element(By.CSS_SELECTOR,"#index-kw").send_keys("tongtong")
sleep(2)
#百度一下点击一下
self.driver.find_element(By.CSS_SELECTOR, "#index-bn").click()
sleep(3)
如何判断页面是不是webview
断网查看,如果断网显示网页加载不了就是webview
看加载条,有加载条通常是webview
看顶部是否有关闭按钮
下拉刷新,页面有刷新就是webview
下拉刷新的时候是否有网页提供者
用工具查看,如果元素显示webview,则是webview
Webview
是android系统提供能显示页面的系统控件(特殊的view)
< android4.4 webview底层实现webkit内部
=android4.4 采用chromium作为webview底层支持,支持html5,css3,js
webaudio:图形化的界面收听音频
webGL:页面3d效果的渲染
webRTC:直播等等,美颜
混合webview测试条件 #注意混合webview测试时 desire_cap设置使用"appPackage"、"appActivity"
#纯webview测试,desire_cap设置不使用"appPackage"、"appActivity",而使用"browserName"
###模拟器可以使用mumu模拟器,也可以使用genymotion模拟器(genymotion模拟器建议分开下载genymotion-3.2.1、VirtualBox(下载地址:https://docs.genymotion.com/desktop/latest/01_Get_started.html#pre-requisites))
PC:
能够访问google
下载对应版本的chromedriver
#使用chrome://inspect/#devices来查看对应的webview页面,只能查看webview页面
手机端:应用代码需要打开webview的开关
客户端:
代码中要添加chromedriverExecutable
有一些webview可以被uiautomatorview查找到,但都不推荐,可能会出现兼容性的问题,比如text的显示字符串会不一样
如何查找当前webview的网页
方案一:
adb shell
logcat | grep http
就能找到访问的http了
方案二(最优的解决方案): #通过浏览器访问chrome://inspect/#devices 监控webview,如果我们手机访问的页面确实是webview,等待30s,chrome://inspect/#devices会出现对应的webview
'''
案例1 appium的api的混合webview
打开api demo的webview
向输入框输入文本
点击i am link
退出应用
'''
from time import sleep
from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
class TestFind():
def setup(self):
self.desire_cap= {
"platformName":"android",
"platformVersion":"6.0",
"deviceName":"127.0.0.1:7555",
"noRest":True,
"appPackage": "io.appium.android.apis",
"appActivity":"io.appium.android.apis.view.WebView1",
#想要切换webview,必须得指定chromdriver,或者你的默认地址的chromedriver的版本和手机的版本是对应的
"chromedriverExecutable": r"c:\chrome\chromedriver.exe"
}
self.driver=webdriver.Remote("http://127.0.0.1:4723/wd/hub",self.desire_cap)
self.driver.implicitly_wait(5)
def teardown(self):
self.driver.quit()
def test_appium_api_webview(self):
sleep(3)
#进入到webview页面,直接打印contexts,肯定有两个,一个是原生的,一个是webview
print(self.driver.contexts)
#需要切换到webview的context的,通常是倒数第一个,记得要有chromedriverExecutable
self.driver.switch_to.context(self.driver.contexts[-1])
sleep(2)
#往输入框输入tongtong
self.driver.find_element(MobileBy.ID,"i_am_a_textbox").send_keys("tongtong")
#点击链接
self.driver.find_element(MobileBy.ID,"i am a link").click()
#打印出当前的页面布局,发现是一个webview的html的布局
print(self.driver.page_source)
'''
案例2 雪球webview
打开应用
点击交易
点击A股开户
输入用户名和密码
点击立即开户
退出应用
注:打开新的页面其实就是一个新的窗口了,要切换窗口句柄了
'''
#由于chrome识别不到雪球的webview,元素定位有问题,所以代码搞不定
from time import sleep
from appium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
class TestFind():
def setup(self):
self.desire_cap= {
"platformName":"android",
"platformVersion":"6.0",
"deviceName":"127.0.0.1:7555",
#想要使用原生的浏览器就选择,Browser。想要选择chrome浏览器就输入Chrome
"browserName":"Browser",
"noRest":True,
#这里是指定chromedriver的路径,记得路径要全到包括chromedriver.exe
"chromedriverExecutable":r"c:\chrome\chromedriver.exe"
}
self.driver=webdriver.Remote("http://127.0.0.1:4723/wd/hub",self.desire_cap)
self.driver.implicitly_wait(5)
def teardown(self):
self.driver.quit()
def test_browser(self):
#打开移动端的百度浏览器
self.driver.get("http://m.baidu.com")
#显示等待找到搜索框是否可见,expected_conditions里面传的locator必须是一个元祖
WebDriverWait(self.driver,10).until(expected_conditions.visibility_of_element_located((By.CSS_SELECTOR,"#index-kw")))
#搜索框输入tongtong
self.driver.find_element(By.CSS_SELECTOR,"#index-kw").send_keys("tongtong")
sleep(2)
#百度一下点击一下
self.driver.find_element(By.CSS_SELECTOR, "#index-bn").click()
sleep(3)
webview遇到的坑
设备
Android模拟器6.0默认支持webview(mumu不可以,genimotion和sdk自带的emulator可以) #后面测试mumu可以
其他模拟器和物理机需要打开app内开关(webview调试开关)
PC浏览器定位元素
chrome浏览器-62版本才可以更好的看见webview的内部,其他的版本都有一些bug
换成chromium浏览器可以避免很多坑,展示效果和速度要chrome要快
代码
有的设备可以使用find_element_acessibility_id(), 不同的设备渲染的页面不同,兼容性不适合
switch_to.context() 切换不同的context,一个页面来说
#手机页面无法确认是否新开了窗口,可以在代码中打印窗口handles
switch.to_window() 切换不同的窗口句柄,对不同的页面来说
Menu:微信小程序测试
from appium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
class TestWXMicroWebView:
# 为了演示方便,未使用page object模式
def setup(self):
caps = {}
caps["platformName"] = "android"
caps["deviceName"] = "测试人社区 ceshiren.com"
caps["appPackage"] = "com.tencent.mm"
caps["appActivity"] = "com.tencent.mm.ui.LauncherUI"
caps["noReset"] = True
caps['unicodeKeyboard'] = True
caps['resetKeyboard'] = True
caps['chromedriverExecutable'] = \
'/Users/seveniruby/projects/chromedriver/chromedrivers/chromedriver_78.0.3904.11'
# options = ChromeOptions()
# options.add_experimental_option('androidProcess', 'com.tencent.mm:appbrand0')
#设置chromeOptions
caps['chromeOptions'] = {
'androidProcess': 'com.tencent.mm:appbrand0'
}
#设置adbPort使用adb代理模式
caps['adbPort'] = 5038
self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
self.driver.implicitly_wait(30)
self.driver.find_element(By.XPATH, "//*[@text='通讯录']")
self.driver.implicitly_wait(10)
self.enter_micro_program()
print(self.driver.contexts)
def enter_micro_program(self):
# 原生自动化测试
size = self.driver.get_window_size()
self.driver.swipe(size['width'] * 0.5, size['height'] * 0.4, size['width'] * 0.5, size['height'] * 0.9)
self.driver.find_element(By.CLASS_NAME, 'android.widget.EditText').click()
self.driver.find_element(By.XPATH, "//*[@text='取消']")
self.driver.find_element(By.CLASS_NAME, "android.widget.EditText").send_keys("雪球")
self.driver.find_element(By.CLASS_NAME, 'android.widget.Button')
self.driver.find_element(By.CLASS_NAME, 'android.widget.Button').click()
self.driver.find_element(By.XPATH, "//*[@text='自选']")
def find_top_window(self):
for window in self.driver.window_handles:
print(window)
if ":VISIBLE" in self.driver.title:
print(self.driver.title)
else:
self.driver.switch_to.window(window)
def test_search_webview(self):
# 进入webview
self.driver.switch_to.context('WEBVIEW_xweb')
self.driver.implicitly_wait(10)
self.find_top_window()
# css定位
self.driver.find_element(By.CSS_SELECTOR, "[src*=stock_add]").click()
# 等待新窗口
WebDriverWait(self.driver, 30).until(lambda x: len(self.driver.window_handles) > 2)
self.find_top_window()
self.driver.find_element(By.CSS_SELECTOR, "._input").click()
# 输入
self.driver.switch_to.context("NATIVE_APP")
ActionChains(self.driver).send_keys("alibaba").perform()
# 点击
self.driver.switch_to.context('WEBVIEW_xweb')
self.driver.find_element(By.CSS_SELECTOR, ".stock__item")
self.driver.find_element(By.CSS_SELECTOR, ".stock__item").click()
Menu:模拟器控制
#模拟器控制
avd #(Android only)要启动的 avd 的名称,如果模拟器未进行开启,设置Capability avd设置,会自动打开模拟器 【Android Studio自带的模拟器支持该Capability设置,其他模拟器(如网易mumu,夜神)不支持】
udid #连接的物理设备的唯一设备标识符 有多个虚拟机时,选择指定的虚拟机用于启动 udid是连接物理设备的唯一设备标识符
#Android通过adb devices来获取设备列表对应的uuid,如果有多台虚拟机,或者一台虚拟机,不进行设置Capability udid设置 默认启动设备列表第一个虚拟机器
#多台设备,不同的设备配置到不同的配置文件里面,让appium进行读取
#iOS获取udid通过Xcode里面的设备管理器avd manager进行获取
Menu:设备交互api
设备交互api
GSM Call #模拟电话接入 https://appium.io/docs/en/commands/device/network/gsm-call/
self.driver.make_gsm_call('5551234567', GsmCallActions.CALL)
Send SMS #模拟短信接收 https://appium.io/docs/en/commands/device/network/send-sms/
self.driver.send_sms('555-123-4567', 'Hey lol')
切换网络模式
self.driver.set_network_connection(1) #1:飞行模式 2:wifi模式 4:流量模式 # 0:没wife 没网络 没流量
Menu:Capability进阶
Capability进阶设置 https://appium.io/docs/en/writing-running-appium/caps/index.html
dontStopAppOnReset #首次启动的时候,不停止app(可以调试或者运行的时候提升运行速度),页面停留在某个页面上会继续进行操作,不会关闭APP后再打开运行
autoGrantPermissions #让 Appium 自动确定您的应用程序需要哪些权限,并在安装时将它们授予应用程序。如果noReset设置true,则此功能不起作用。
newCommandTimeout #在假设客户端退出并结束会话之前,Appium 将等待来自客户端的新命令多长时间(以秒为单位) 防止程序还未开始运行出现异常
使用场景:
例如文件上传,比如上传视频文件设置等待时间长度3000s
或者是APK安装,APK安装设置等待时间长度3000s