目录:
1.appium server 环境安装
安装 Node.js
// 查看源
npm config get registry
//更换源
npm config set registry https://registry.npm.taobao.org
安装成功后的截图:
安装 Appium Server
// 安装最新版本的appium server
npm install -g appium
// 注意 -g 参数一定要有,不能省,代表全局
// 安装某个特定的版本
npm install -g [email protected]
安装环境检测工具
# 安装检测工具 appium-doctor
npm install -g appium-doctor
appium-doctor
运行 Appium 服务
// 帮助文档
appium --help
// 运行 appium 服务
appium
2.capability 进阶用法
deviceName
uid
多设备选择的时候,要指定 uid
默认读取设备列表的第一个设备
设备列表获取
adb devices
newCommandTimeout
60
秒autoGrantPermissions
PRINT_PAGE_SOURCE_ON_FIND_FAILURE
false
测试策略
noReset
false
true
fullReset
默认为 false
。true
:新会话之前完全卸载被测应用程序
安卓
dontStopAppOnReset
false
。true
#打开的app退出后重新启动
adb shell am start -S 包名/activity名
#打开的app不需要退出,直接使用当前页面
adb shell am start 包名/activity名
性能相关
3.元素定位工具
uiautomatorviewer 工具安装(不推荐)
tools/bin
目录下的 uiautomatorviewer
程序uiautomatorviewer 工具功能介绍
weditor 安装与运行
pip install weditor
进行安装python -m weditor
即可weditor 功能介绍
appium inspector 安装与运行
appium inspector 功能简介
对选中元素操作
4.高级定位技巧-xpath 定位
xpath 函数
包含-contains()
Xpath
表达式中的一个函数
contains()
函数匹配==属性值==中包含的==字符串==
//*[contains(@属性,"属性值")]
xpath 轴
父子-当前节点的父节点
//*[@text="HK"]/..
//*[@text="HK"]/parent::*
父子-当前节点的子节点
//*[@resource-id="com.xueqiu.android:id/stock_layout"]/child::*
爷孙-当前节点的爷爷
//*[@text="HK"]/../..
//*[@text="HK"]/parent::*/parent::*
爷孙-当前节点的孙子
//*[@resource-id="com.xueqiu.android:id/stock_layout"]/child::*/child::*
祖先-ancestor
返回当前节点的所有祖先
//*[@text="HK"]/ancestor::android.widget.RelativeLayout
显式指定要返回的祖先
//*[@text="HK"]/ancestor::android.widget.RelativeLayout[1]
兄弟姐妹-sibling
following-sibling
选择当前节点之后的所有兄弟节点
节点后有一个兄弟节点
//*[@text="HK"]/following-sibling::*
节点后有多个兄弟节点
//*[@resource-id="com.xueqiu.android:id/stock_layout"]/following-sibling::*[@resource-id="com.xueqiu.android:id/price_layout
preceding-sibling
选择当前节点之前的所有兄弟节点
节点前有一个兄弟节点
//*[@text="09988"]/preceding-sibling::*
节点前有多个兄弟节点
//*[@resource-id="com.xueqiu.android:id/add_attention"]/preceding-sibling::*[@resource-id="com.xueqiu.android:id/price_layou
XPath 运算符
AND
可以在 XPath
表达式中放置 2 个条件
在 AND
两个条件都应该为真的情况下,才能找到元素
//*[@resource-id="com.xueqiu.android:id/current_price" and @text="107.8"]
OR
可以在 XPath
表达式中放置 2 个条件
在 OR
的情况下,两个条件中的任何一个为真,就可找到元素。
OR
定位获取的是并集
//*[@resource-id="com.xueqiu.android:id/tv_stock_add_follow" or @text="加自选"]
5.高级定位技巧-css 定位与原生定位
原生定位
# ID 定位
driver.find_element_by_android_uiautomator('new UiSelector().resourceId("")')
# 组合定位
driver.find_element_by_android_uiautomator('new UiSelector().resourceId("com.xueqiu.android:id/tab_name").text("我的")')
css selector 定位介绍
代码:
driver.find_element(AppiumBy.CSS_SELECTOR,"#com\.xueqiu\.android\:id\/tv_search")
解析前:
{"using":"css selector","value":"#com\\.xueqiu\\.android\\:id\\/tv_search"}
解析后:
{"strategy":"-android uiautomator","selector":"new UiSelector().resourceId(\"com.xueqiu.android:id/tv_search\")",...}
css selector 用法
# 通过 id
elementById("someResourceID")`
-> `elementsByCss("#someResourceID")
# 通过 class
elementsByClassName("android.widget.TextView")`
-> `elementsByCss("android.widget.TextView")
# 通过 accessibility id
elementsByAccessibilityId("Some Content Description")`
-> `elementsByCss('*[description="Some Content Description"]')
# 通过 xpath
elementsByXpath("//android.widget.TextView[@description='Accessibility']")`
-> `elementsByCss("android.widget.TextView[description='Accessibility']")
iOS css selector 定位
总结
6.特殊控件 toast 识别
Toast 是什么
一种消息框类型
永远不会获得焦点,无法被点击
Toast显示的时间有限,Toast会根据用户设置的显示时间后自动消失
是系统级别的控件,属于系统settings
Toast类的思想:就是尽可能不引人注意,同时还向用户显示信息,希望他们看到
Toast 定位
Toast 定位
xpath
可以找到 ``` //*[@class="android.widget.Toast"]
//*[contains(@text,"xxx")] ``` xxx:toast的文本内容
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(50));
driver.findElement(AppiumBy.xpath("//*[@class=\"android.widget.Toast\"]"));
代码示例:
from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
class TestToast():
def setup(self):
desire = {
'platformName': 'android',
'platformVersion': '6.0',
'deviceName': 'emulator-5554',
'appPackage': 'io.appium.android.apis',
'appActivity': 'io.appium.android.apis.view.PopupMenu1',
'automationName': 'uiautomator2'
}
self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desire)
self.driver.implicitly_wait(5)
def teardown(self):
self.driver.quit()
def test_toast(self):
self.driver.find_element(MobileBy.ACCESSIBILITY_ID, "Make a Popup!").click()
self.driver.find_element(MobileBy.XPATH, "//*[@text='Search']").click()
print(self.driver.find_element(MobileBy.XPATH, "//*[@class='android.widget.Toast']").text)
# print(self.driver.find_element(MobileBy.XPATH, "//*[contains(@text, 'Clicked popup')]").text)
7.显式等待高级使用
Wait 等待
driver.implicitly_wait(TIMEOUT)
WebDriverWait(self.driver,10,0.5).until(expected_conditions.visibility_of_element_located(LOCATOR))
显式等待
NoSuchElementException
WebDriverWait
和 expected_conditions
两个类显式等待
WebDriverWait 用法
expected_conditions 类
presence_of_element_located
判断元素是否被加到了 DOM 树里,并不代表该元素一定可见
WebDriverWait().until(expected_conditions.presence_of_element_located(元素对象))
visibility_of_element_located
判断某个元素是否可见,可见代表元素非隐藏,并且元素的宽和高都不等于 0
WebDriverWait().until(expected_conditions.visibility_of_element_located(元素定位符))
使用 lambda 表达式
WebDriverWait(driver,time).until(lambda x:x.find_element_by_id("someId")
总结三种等待方式
8.高级控件交互方法
Actions
W3C 事件流
用法
代码
...
# 定义ActionChains 实例
actions = ActionChains(driver)
# 第一步:定义输入源
# ActionChains里有个属性是ActionBuilder类型的, 使用的就是w3c协议
# 可以定义鼠标指针源,键盘源,滚轮源事件
actions.w3c_actions = ActionBuilder(driver, mouse=PointerInput(interaction.POINTER_TOUCH, "touch"))
# 第二步:定义动作
# 移动到起点-> 按下-> 滑动-> 抬起
actions.w3c_actions.pointer_action.move_to_location(115, 183)
actions.w3c_actions.pointer_action.pointer_down()
actions.w3c_actions.pointer_action.move_to_location(362, 179)
actions.w3c_actions.pointer_action.release()
actions.perform()
...
练习
代码示例:
import time
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver import ActionChains
from selenium.webdriver.common.actions import interaction
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver.common.actions.pointer_input import PointerInput
class TestAction:
def setup(self):
caps = {}
caps["platformName"] = "Android"
caps["appPackage"] = "cn.kmob.screenfingermovelock"
caps["appActivity"] = "com.samsung.ui.FlashActivity"
caps["deviceName"] = "127.0.0.1:7555"
caps["ensureWebviewsHavePages"] = True
caps["noReset"] = "true"
self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
self.driver.implicitly_wait(15)
def teardown(self):
self.driver.quit()
def test_action(self):
el1 = self.driver.find_element(AppiumBy.ID,"cn.kmob.screenfingermovelock:id/patternTxt")
el1.click()
# 定义ActionChains 实例
actions = ActionChains(self.driver)
# 第一步:定义输入源
# ActionChains里有个属性是ActionBuilder类型的, 使用的就是w3c协议
# 可以定义鼠标指针源,键盘源,滚轮源事件
actions.w3c_actions = ActionBuilder(self.driver, mouse=PointerInput(interaction.POINTER_TOUCH, "touch"))
# 第二步:定义动作
# 移动到起点-> 按下-> 滑动-> 抬起
actions.w3c_actions.pointer_action.move_to_location(118, 176)
actions.w3c_actions.pointer_action.pointer_down()
actions.w3c_actions.pointer_action.move_to_location(355, 176)
actions.w3c_actions.pointer_action.pause(0.5)
actions.w3c_actions.pointer_action.move_to_location(751, 221)
actions.w3c_actions.pointer_action.pause(0.5)
actions.w3c_actions.pointer_action.move_to_location(752, 518)
actions.w3c_actions.pointer_action.pause(0.5)
actions.w3c_actions.pointer_action.move_to_location(751, 821)
actions.w3c_actions.pointer_action.release()
actions.perform()
time.sleep(2)
9.设备交互api
常用的设备交互命令(建议去appium官网查看文档)
模拟电话、短信
driver.makeGsmCall(PHONE_NUMBER, GsmCallActions.CALL);
driver.makeGsmCall(PHONE_NUMBER, GsmCallActions.ACCEPT);
driver.makeGsmCall(PHONE_NUMBER, GsmCallActions.CANCEL);
driver.sendSMS("555-123-4567", “Appium Test”);
网络设置
def set_network_connection(self, connection_type: int) -> int:
"""Sets the network connection type. Android only.
Possible values:
+--------------------+------+------+---------------+
| Value (Alias) | Data | Wifi | Airplane Mode |
+====================+======+======+===============+
| 0 (None) | 0 | 0 | 0 |
+--------------------+------+------+---------------+
| 1 (Airplane Mode) | 0 | 0 | 1 |
+--------------------+------+------+---------------+
| 2 (Wifi only) | 0 | 1 | 0 |
+--------------------+------+------+---------------+
| 4 (Data only) | 1 | 0 | 0 |
+--------------------+------+------+---------------+
| 6 (All network on) | 1 | 1 | 0 |
+--------------------+------+------+---------------+
self.driver.set_network_connection(1)
self.driver.set_network_connection(4)
横竖屏切换
driver.rotate(Screenorientation.LANDSCAPE)
driver.rotate(Screenorientation.PORTRAIT)
获取日志
self.driver.log_types
self.driver.get_log("logcat")
其它常用操作
driver.lock()
driver.get_screenshot_as_file('./photos/img.png')
self.driver.start_recording_screen()
self.driver.stop_recording_screen()
代码示例:
import time
from appium import webdriver
from appium.webdriver.extensions.android.gsm import GsmCallActions
class TestDevice:
def setup(self):
caps = {}
caps["platformName"] = "Android"
caps['platformVersion'] = '6.0'
caps["appPackage"] = "cn.kmob.screenfingermovelock"
caps["appActivity"] = "com.samsung.ui.FlashActivity"
caps["deviceName"] = "127.0.0.1:7555"
caps["ensureWebviewsHavePages"] = True
caps["noReset"] = "true"
caps['unicodeKeyBoard'] = 'true'
caps['resetKeyBoard'] = 'true'
self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
self.driver.implicitly_wait(15)
def teardown(self):
self.driver.quit()
def test_mobile(self):
self.driver.make_gsm_call('15033657989', GsmCallActions.CALL)
self.driver.send_sms('15033657956', 'hello appium api')
time.sleep(5)
self.driver.set_network_connection(1)
time.sleep(2)
self.driver.set_network_connection(4)
time.sleep(2)
self.driver.get_screenshot_as_file('./datas/screenshot/img.png')
10.模拟器控制
android 模拟器创建
配置
代码示例:
test_simulator_control.py
import time
from appium import webdriver
class TestControl:
def setup(self):
caps = {}
caps["platformName"] = "Android"
caps['platformVersion'] = '6.0'
caps["appPackage"] = "cn.kmob.screenfingermovelock"
caps["appActivity"] = "com.samsung.ui.FlashActivity"
caps["deviceName"] = "127.0.0.1:7555"
caps["ensureWebviewsHavePages"] = True
caps['unicodeKeyBoard'] = 'true'
caps['resetKeyBoard'] = 'true'
caps["noReset"] = "true"
caps['avd'] = 'Resizable_Experimental_API_34'
self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
self.driver.implicitly_wait(15)
def teardown(self):
self.driver.quit()
def test_control(self):
self.driver.set_network_connection(1)
time.sleep(2)
self.driver.set_network_connection(4)
time.sleep(2)
self.driver.get_screenshot_as_file('./datas/screenshot/img.png')
11.雪球财经app股票详情功能点自动化测试实战
产品分析
Page Object 模式六大原则
总结
用例设计