android 基础知识
-
Android 是通过容器的布局属性来管理子控件的位置关系,布局关系就是把界面上的所有的空间,根据他们的间距的大小,摆放在正确的位置
-
Android 七大布局
- LinerLayout(线性布局)
- RelativeLayout(相对布局)
- FrameLayout(帧布局)
- AboluteLayout(绝对布局)
- TableLayout(表格布局)
- GridLayout(网格布局)
- ConstraintLayout(约束布局
-
Android 四大组件
- activity 与用户交互的可视化界面
- service 实现程序后台运行的解决方案
- content provider 内容提供者,提供程序所需要的数据
- broadcast receiver 广播接收器,监听外部事件的到来(比如来电)
-
常用的控件
- TextView(文本控件),EditText(可编辑文本控件)
- Button(按钮),ImageButton(图片按钮),ToggleButton(开关按钮)
- ImageView(图片控件)
- CheckBox(复选框控件),RadioButton(单选框控件)
-
布局
- 是可用于放置很多控件的容器按照一定的规律调整内部控件的位置由此构成界面。
-
嵌套布局
- 布局内部放置布局,多层布局嵌套,可以完成复杂的界面结构
ios 基础知识
-
布局
- iOS 不使用布局的概念,用变量之间的相对关系完成位置的计算
-
注意
- 使用 Appium 测试 iOS 应用需要使用 MacOS 操作系统
元素定位
-
概念:元素定位的含义就是定位控件
-
注意:同一脚本同时支持 android/iOS 两个系统的前提是
元素属性(id,aid,xpath 等)一致
控件基础知识
-
dom:Document Object Model 文档对象模型
-
dom 应用:用于表示界面的控件层级,界面的结构化描述
- 常见的格式:html、xml
- 核心元素:节点、属性
-
xpath:xml 路径语言,用于 xml 中的节点定位
控件基础知识
- Anrdroid 应用的层级结构与 html 不一样,是一个定制的 xml
- app source 类似于 dom ,表示 app 的层级,代表了界面里面所有的控件树的结构
- 每个控件都有它的属性(resourceid,xpath,aid),但是没有 css 属性
app dom 结构解析
app dom 示例
-
node
-
attribute
- clickable
- content-desc
- resource-id
- text
- bounds
iOS 与 Android dom 结构的区别
- dom 属性和节点结构类似
- 名字和属性命名不同
- android 的 resourceid 和 ios 的 name
- android 的 content-desc 和 ios 的 accessibility-id
定位方法
-
测试步骤三要素
- 定位、交互、断言
-
定位方式:
- id 定位
- accessibilty_id 定位
- xpath 定位
- classname 定位(不推荐)
常见的控件定位方法
App 定位方式
定位策略 | 描述 |
---|---|
Accessibility ID | 识别一个唯一的 UI 元素,对于 XCUITest 引擎,它对应的的属性名是 accessibility-id ,对于 Android 系统的页面元素,对应的属性名是 content-desc |
Class name | 对于 iOS 系统,它的 class 属性对应的属性值会以XCUIElementType 开头,对于 Android 系统,它对应的是 UIAutomator2 的 class 属性(e.g.: android.widget.TextView) |
ID | 原生元素的标识符,Android 系统对应的属性名为resource-id ,iOS 为name |
Name | 元素的名称 |
XPath | 使用 xpath 表达式查找页面所对应的 xml 的路径(不推荐,存在性能问题) |
App 定位方式进阶
定位策略 | 描述 |
---|---|
Image | 通过匹配 base 64 编码的图像文件定位元素 |
Android UiAutomator (UiAutomator2 only) | 使用 UI Automator 提供的 API, 尤其是 UiSelector 类来定位元素,在 Appium 中,会发送 Java 代码作为字符串发送到服务器,服务器在应用程序的环境中执行这段代码,并返回一个或多个元素 |
Android View Tag (Espresso only) | 使用 view tag 定位元素 |
Android Data Matcher (Espresso only) | 使用 Espresso 数据匹配器定位元素 |
IOS UIAutomation | 在 iOS 应用程序自动化时,可以使用苹果的 instruments 框架查找元素 |
选择定位器通用原则
- 与研发约定的属性优先
- android 推荐 content-description
- ios 推荐 label
- 身份属性 id
- 组合定位 xpath,css
- 其它定位
元素定位的写法
- 返回单个元素 WebElement
- 返回元素列表 [WebElement, WebElement, WebElement…]
# 返回单个元素 WebElement
driver.find_element(AppiumBy.xxx, "xxx属性值")
# 返回元素列表 [WebElement, WebElement, WebElement...]
driver.find_elements(AppiumBy.xxx, "xxx属性值")
元素定位的写法
driver.find_element(AppiumBy.ID, "ID属性值")
driver.find_element(AppiumBy.XPATH, "xpath表达式")
driver.find_element(AppiumBy.CLASS_NAME, "CLASS属性值")
driver.find_element(AppiumBy.ACCESSIBILITY_ID, "ACCESSIBILITY_ID表达式")
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, "android uiautomator 表达式")
driver.find_element(AppiumBy.IOS_UIAUTOMATION, "ios uiautomation 表达式")
driver.find_element(AppiumBy.ANDROID_VIEWTAG, "ESPRESSO viewtag 表达式")
driver.find_element(AppiumBy.ANDROID_DATA_MATCHER, "ESPRESSO data matcher 表达式")
driver.find_element(AppiumBy.IMAGE, "IMAGE图片")
元素定位的写法
driver.find_elements(AppiumBy.ID, "ID属性值")
driver.find_elements(AppiumBy.XPATH, "xpath表达式")
driver.find_elements(AppiumBy.CLASS_NAME, "CLASS属性值")
driver.find_elements(AppiumBy.ACCESSIBILITY_ID, "ACCESSIBILITY_ID表达式")
driver.find_elements(AppiumBy.ANDROID_UIAUTOMATOR, "android uiautomator 表达式")
driver.find_elements(AppiumBy.IOS_UIAUTOMATION, "ios uiautomation 表达式")
driver.find_elements(AppiumBy.ANDROID_VIEWTAG, "ESPRESSO viewtag 表达式")
driver.find_elements(AppiumBy.ANDROID_DATA_MATCHER, "ESPRESSO data matcher 表达式")
driver.find_elements(AppiumBy.IMAGE, "IMAGE图片")
ID 定位
- 通过身份标识 id 查找元素
- 写法:
find_element(AppiumBy.ID, "ID属性值")
ACCESSIBILITY_ID 定位
- 通过 accessibility id 查找元素
- 写法:
find_element(AppiumBy.ACCESSIBILITY_ID, "ACCESSIBILITY_ID属性值")
XPath 定位
表达式 | 描述 |
---|---|
/ | 从根节点选取(取子节点)。 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置(取子孙节点)。 |
. | 选取当前节点。 |
.. | 选取当前节点的父节点。 |
@ | 选取属性。 |
XPath 单属性定位
- 基本表达式:
//*[@属性名='属性值']
XPath 多属性定位
- 表达式:
//*[@属性名='属性值' and @属性名='属性值' ]
实战练习
- 安装 ApiDemo.apk - 链接: https://pan.baidu.com/s/1Zr-3Z1plg6fysvFpmxnk4A 密码: gdcw
- 打开应用
- 定位文字为【App】元素
class TestLocation:
def setup(self):
caps = {}
caps["platformName"] = "Android"
caps["appium:appPackage"] = "io.appium.android.apis"
caps["appium:appActivity"] = ".ApiDemos"
caps["appium:deviceName"] = "127.0.0.1:7555"
caps["dontStopAppOnReset"] = "true"
caps["noReset"] = "true"
self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", caps)
self.driver.implicitly_wait(5)
def teardown(self):
self.driver.quit()
def test_id(self):
"""通过 ID 进行元素定位"""
print(self.driver.find_element(AppiumBy.ID, "android:id/text1"))
def test_aid(self):
"""通过 ACCESSIBILITY_ID 进行元素定位"""
print(self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "App"))
def test_xpath(self):
"""通过 XPATH 进行元素定位"""
print(self.driver.find_element(AppiumBy.XPATH, "//*[@text='App']"))
def test_xpath1(self):
"""通过 XPATH 进行元素定位"""
print(self.driver.find_element(AppiumBy.XPATH, "//*[@text='App' and @resource-id='android:id/text1']"))
原生定位
Android 原生定位
- 元素属性定位
- ID 定位
- 文本定位
- 文本匹配定位
- 父子关系定位
- 兄弟关系定位
Android 原生定位 - 单属性定位
- 格式
'new UiSelector().属性名("<属性值>")'
- 比如:
'new UiSelector().resourceId("android:id/text1")'
- 比如:
- 注意外面是单引号,里面是双引号,顺序不能变
- 可以简写为
属性名("<属性值>")'
- 比如:·
resourceId("android:id/text1")
- 比如:·
# ID 定位
def test_android_uiautomator_by_id(self):
print(self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,\
'new UiSelector().resourceId("android:id/text1")'))
# TEXT 定位
def test_android_uiautomator_by_text(self):
print(self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,\
'new UiSelector().text("App")'))
# classname 定位
def test_android_uiautomator_by_className(self):
print(self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, \
'new UiSelector().className("android.widget.TextView")'))
Android 原生定位-组合定位
- 多个属性同时确定元素的(多个属性任意组合 ,不限长度)
driver.find_element_by_android_uiautomator('\
new UiSelector().resourceId("com.xueqiu.android:id/tab_name").\
text("我的")')
Android 原生定位-模糊匹配
- 文字包含
- 文字以 x 开头
- 文字正则匹配
# 模糊匹配
def test_android_uiautomator_by_text_contains(self):
print(self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().textContains("ssi")').text)
def test_android_uiautomator_by_text_start_with(self):
print(self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().textStartsWith("Ani")').text)
def test_android_uiautomator_by_text_match(self):
print(self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().textMatches("^Pre.*")').text)
Android 原生定位-层级定位
- 兄弟元素定位
fromParent
- 父子结点定位
childSelector
, 可以传入 resourceId() , description() 等方法
# 查找目标元素Text,先找App ,fromParent() 方法可以查找兄弟结点
new UiSelector().text("App").fromParent(text("Text"))
# 根据父结点查找子结点/ 子孙结点
new UiSelector().className("android.widget.ListView").childSelector(text("Text"))
滑动查找元素
new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text("查找的元素文本").instance(0))
总结
- Appium 提供多种元素定位方式,id,xpath, class, 也可以通过 Android Uiautomator 定位,或 iOS Predicate
- xpath 是比较灵活的定位方式(后面有章节详细介绍高级用法)
- 原生定位了解即可