自动化工具选择
工具名称 | 被测系统 | 测试 | 脚本语言 | 支持H5 | 跨应用 | 稳定性 | SDK自带 |
---|---|---|---|---|---|---|---|
MonKeyRunner | Android | 功能 | Python | 支持 | 否 | 稳定 | 是 |
Instrumentation | Android(<4.1) | 功能 | Java | 支持 | 可以 | 稳定 | 否 |
Uiautomator2 | Android(>=4.1) | 功能 | Java | 支持 | 可以 | 稳定 | 是 |
Adb-For-Test | Android(>=4.1) | 功能 | Java/Python | 支持 | 可以 | 稳定 | 否 |
Monkey | Android | 稳定 | Java | 否 | 否 | 稳定 | 是 |
CTS | Android | 兼容 | Java | 支持 | 可以 | 稳定 | 否 |
Uiautomation | iOS | 功能 | JS | 支持 | 可以 | 稳定 | Xcode自带 |
Calabash | Android,iOS | 功能 | Ruby | 支持 | 可以 | 一般 | 否 |
Appium | Android,iOS | 功能 | Java/Python/jS/C/c#/Perl | 支持 | 可以 | 一般 | 否 |
Appium生态工具
- adb:Android的控制工具,用于获取Android的各种数据和控制
- Appium Desktop:内嵌了appium server和inspector的综合工具
- Appium Server:appium的核心工具、命令行工具
- Appium client:各种语言的客户端封装库,用于连接appium server
- python、java、ruby、robotframework-appium
- AppCrawlar自动遍历工具
环境安装
- Java 1.8版本(配置环境变量)
- Android SDK(配置环境变量)
- Appium Desktop
- Python3
- Appium python client
Android自动化前提依赖
- android sdk:Android Studio可辅助安装
- 模拟器
- Android Studio⾃带emulator [推荐]
- Genymotion
- BlueStacks
- 真机
- Appium Desktop:⼊门学习⼯具
获取app的信息
- app信息
- 获取当前界⾯元素:adb shell dumpsys activity top
- 获取任务列表:adb shell dumpsys activity activities
- app⼊口
- adb logcat |grep -i displayed
- aapt dump badging mobike.apk | grep launchable-activity
- apkanalyzer 最新版本的sdk中才有
- 启动应⽤
- adb shell am start -W -n com.xueqiu.android/.view.WelcomeActivityAlias -S
Android的capability
启动app后执行命令adb logcat | grep Displayed
,获取到app的包名和activityid:包名/activityid
Appium需要设置好设备信息:Automatic Server->Desired Capabilities
from appium import webdriver
caps = {}
caps["platformName"] = "android"
caps["deviceName"] = "test"
caps["appPackage"] = "com.xueqiu.android"
caps["appActivity"] = ".view.WelcomeActivityAlias"
caps['autoGrantPermissions'] = True
caps['noreset']=True
caps['automationName']='uiautomator2'
driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
self.driver.implicitly_wait(5)
Capability 简介
- 功能:配置 Appium 会话,告诉 Appium 服务器需要自动化的平台的应用程序
- 形式:键值对的集合,键对应设置的名称,值对应设置的值
platformName:平台,Android/iOS
deviceName:设备名
appPackage:应用的包名
appActivity:应用的页面名 Activity
noReset: 防止清空缓存信息
主要分为三部分
- 公共部分
- ios 部分
- android 部分
Session
- Appium 的客户端和服务端之间进行通信的前提
- 通过 Desired Capabilities 建立会话
公共部分参数配置
键 | 描述 | 值 |
---|---|---|
platformName | 使用的手机操作系统 | iOS,Android,或者 Firefox0S |
platformVersion | 手机操作系统的版本 | 例如 7.1, 4.4 |
deviceName | 使用的手机或模拟器类型 | iPhone Simulator, iPad Simulator, iPhone Retina 4-inch, Android Emulator, Galaxy S4, 等等…. 在 iOS 上,使用 Instruments的 instruments -s devices 命令可返回一个有效的设备的列表。在 Andorid 上虽然这个参数目前已被忽略,但仍然需要添加上该参数 |
automationName | 使用哪个自动化引擎 | android默认使用uiautomator2,ios默认使用XCUTest |
noReset | 在当前 session 下不会重置应用的状态。默认值为 false | true, false |
udid | 连接的真实设备的唯一设备编号 (Unique device identifier) | 例如 1ae203187fc012g |
Android 部分特有参数配置
键 | 描述 | 值 |
---|---|---|
appActivity | Activity 的名字是指从你的包中所要启动的 Android acticity。他通常需要再前面添加. | (例如 使用 .MainActivity 代替 MainActivity) MainActivity, .Settings |
appPackage | 运行的 Android 应用的包名 | com.example.android.myApp, com.android.settings |
appWaitActivity | 用于等待启动的 Android Activity 名称 | SplashActivity |
unicodeKeyboard | 启用 Unicode 输入,默认为 false | true or false |
resetKeyboard | true or false | |
dontStopAppOnReset | 首次启动的时候,不停止 app | true or false |
skipDeviceInitialization | 跳过安装,权限设置等操作 | true or false |
iOS 独有
键 | 描述 | 值 |
---|---|---|
bundleId | 被测应用的 bundle ID 。用于在真实设备中启动测试,也用于使用其他需要 bundle ID 的关键字启动测试。在使用 bundle ID 在真实设备上执行测试时,你可以不提供 app 关键字,但你必须提供 udid 。 | 例如 io.appium.TestApp |
autoAcceptAlerts | 当 iOS 的个人信息访问警告 (如 位置、联系人、图片) 出现时,自动选择接受( Accept )。默认值 false | true 或者 false |
showIOSLog | 是否在 appium 日志中显示从设备捕获的任何日志。 | 默认 false |
配置优化
- 添加参数,提高用例的稳定性
{
"noReset": "true", // 不清空缓存信息
"dontStopAppOnReset": "true", // 首次启动的时候,不停止app
"skipDeviceInitialization": "true", // 跳过安装,权限设置等操作
"unicodeKeyBoard": "true" // 输入中文
}
appium功能键
- SelectElements:选中元素,查看层级和属性
- Swipe By Coordinates:通过坐标点滑动
- Tap By Coordinates:通过坐标点点击
- Back:返回
- Refresh Source & Screenshot:刷新页面
- StartRecording:开始录制脚本
- Search for element:搜索元素
- Copy XML Source to Clipboard:复制 xml 结构
- Quit Session & Close Inspector:退出当前 Session
元素定位方式
1、ID定位,两种方法
find_element_by_id('user_login')
find_element(By.ID, 'user_login')
2、name定位
find_element_by_name('commit')
find_element(By.NAME, 'commit')
3、class定位
find_element_by_class_name('filter')
find_element(By.CLASS_NAME, 'user_login')
4、tag定位
find_element_by_tag_name()
find_element(By.TAG_NAME, 'user_login')
5、link定位
find_element_by_link_text()
find_element(By.LINK_TEXT, '社区')
6、partial link定位
find_element_by_partial_link_text()
find_element(By.PARTIAL_LINK_TEXT, 'user_login')
7、XPath定位
find_element(By.XPATH, "//a[contains(@title,'20190728')]")
find_element_by_xpath("//a[@id='user_login']")
find_element_by_xpath("//a[@name='user_login']")
find_element_by_xpath("//a[@class='user_login']")
find_element_by_xpath("//*[@id='user_login']")
find_element_by_xpath("//a[@type='submit']")
find_element_by_xpath('//a[contains(text(), "问卷")]/a')
//表示当前页面某个目录下, a表示定位元素的标签名,[@id="user_login"]表示这个元素的id属性值,(@title,'20190728')表示这个元素的title属性值,如果不想指定标签名,则可以用星号(*)代替,元素的任意属性值都可以使用,只要它能唯一的标识一个元素。
- 层级与属性结合
如果一个元素本身没有可以唯一标识这个元素的属性值,那么我们可以找其上一级元素,或上上级元素。
find_element_by_xpath("//span[@class='class名称']/span/input") - 使用逻辑运算符
如果一个属性不能唯一区分一个元素,我们还可以使用逻辑运算符连接多个属性来查找元素
find_element_by_xpath("//input[@id='值' and @class='值']/span/input")
8、CSS选择器定位
find_element(By.CSS_SELECTOR, '.filter.nav.nav-pills .active')
find_element(By.CSS_SELECTOR, '.title [title*="先到先得')
find_element(By.CSS_SELECTOR, '.media-heading>span')
find_element(By.CSS_SELECTOR, 'a[href$="82"]')
find_element(By.CSS_SELECTOR, 'a:nth-child(2)') # a标签的第二个子元素
CSS | XPATH |
---|---|
.title a | //div/a |
[attribute*="subString"] | //*[contains(@attribute,"sub")] |
9、定位一组元素
获取标签名为a的所有元素,获取链接中包含12345的元素
hrefs = self.driver.find_elements(By.TAG_NAME, 'a')
for href in hrefs:
if href.get_attribute('title') == '先到先得':
input.click()
10、MobileBy
from selenium.webdriver.common.by import By
class MobileBy(By):
IOS_PREDICATE = '-ios predicate string'
IOS_UIAUTOMATION = '-ios uiautomation'
IOS_CLASS_CHAIN = '-ios class chain'
ANDROID_UIAUTOMATOR = '-android uiautomator'
ANDROID_VIEWTAG = '-android viewtag'
ACCESSIBILITY_ID = 'accessibility id'
IMAGE = '-image'
CUSTOM = '-custom'
继承的是By,所以用法和By是一样的,示例:
self.driver.find_element(MobileBy.XPATH, "//*[@text='交易']")
测试用例重要组成部分
- 导入依赖:
- python:from import
- java:import
- capabilities设置
- python:dict
- java:class
- 初始化driver
- python:webdriver.remote
- java:new AppiumDriver
- 元素定位与操作:find+action
- 断言:assert
capabilities设置
- app apk地址
- appPackage 包名
- appActivity Activity名字
- automationName 默认使⽤uiautomator
- noReset fullReset 是否在测试前后重置相关环境
- unicodeKeyBoard resetKeyBoard 是否需要输⼊⾮英⽂
之外的语⾔并在测试完成后重置输⼊法
控件基础知识
- dom:Document Object Model ⽂档对象模型
- dom应⽤:最早应⽤于html和js的交互。界⾯的结构化描述,常见的格式为html、xml。核⼼元素为节点和属性
- xpath:xml路径语⾔,⽤于xml中的节点定位
app dom为例: - node
- attribute
- clickable
- content-desc
- resource-id
- text
- bounds
- iOS与Android的区别
- dom属性和节点结构类似
- 名字和属性的命名不同
常用api
- click:点击
- send_keys:输入
- get_attribute:获取元素属性
- page source:
def test_profile(self):
self.driver.find_element_by_id("user_profile_icon").click()
def test_click(self):
self.driver.tap() # 根据位置进行点击,参数是坐标位置
def test_sendkeys(self):
self.driver.keyevent() # 键盘控件,输入内容
def test_get_attribute(self):
print(self.driver.find_element_by_id("user_profile_icon").get_attribute("class"))
def test_source(self):
print(self.driver.find_element_by_id("user_profile_icon").get_attribute("class"))
print(self.driver.page_source)
def test_selected_delete(self):
pass
#self.driver.find_element_by_xpath("//*[@text='行情']").click()
#self.driver.find_element_by_xpath("//*[contains(@text, '新增手势')]").click()
def test_swipe(self):
for i in range(5):
self.driver.swipe(500, 900, 100, 200, 1000)
TouchAction
- 长按
- 滑动
定位
-
测试步骤三要素:
- 定位、交互、断⾔
id
aid
xpath
uiautomator
-
accessibityId: content-desc
不推荐:class -ios -android
toast识别
- toast原理
- toast识别方法