测试手册¶
优秀的测试工程师,必定是一个深刻了解软件工程的人,是一个比产品更了解需求的人!
- 作者:一恒
- 微信公众号: 测乎
第二章: Android 测试¶
Android测试,发版前要对渠道版本进行渠道号验证、冒烟测试、签名检查!
android adb 命令使用记录¶
获取信息¶
获取手机系统系的信息,包括硬件和软件
adb shell getprop
adb shell getprop ro.build.version.release #获取手机android系统版本
adb shell getprop ro.build.fingerprint #获取手机品牌型号
adb shell getprop ro.serialno #获取手机设备ID
adb shell getprop dhcp.wlan0.ipaddress #获取当前连接的无线的ip地址
adb shell getprop ro.product.cpu.abilist #获取cpu
包管理¶
a. adb shell pm¶
列出手机中所有的第三方包
adb shell pm list package -3 | sort
列出手机中的系统package
adb shell pm list package -s | sort
清除app缓存数据
adb shell pm clear PackageName
文件管理¶
验证Android渠道版本渠道号¶
国内市场上有许许多多的应用市场,常见的有:百度、360、腾讯应用宝、豌豆荚等。 其他手机厂家如小米、华为、魅族、三星等都有自己的应用市场,总共有上百家!
怎么做¶
Android Apk的渠道号一般存放在AndroidManifest.xml文件中。
- 批量反编译Android Apk
- 遍历反编译后的apk文件夹,从AndroidManifest.xml取出渠道号
- 比较渠道号与apk名称
- 将测试结果写入csv文件
a. 反编译Android Apk¶
反编译Apk,得到源文件和资源文件.
渠道号存放在 AndroidManifest.xml 文件中。
反编译工具apktool.jar: https://bitbucket.org/iBotPeaches/apktool/downloads
java -jar apktool.jar d -f package.apk
输出结果如下:
I: Using Apktool 2.1.0 on test.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: C:\Users\Administrator\apktool\framework\1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Baksmaling classes.dex...
I: Copying assets and libs...
I: Copying unknown files...
I: Copying original files...
备注: Android apk整体反编译所需时间较长,如只获取AndroidManifest.xml文件,那么只需反编译 资源文件即可,不对源代码进行反编译,使用参数 -s ,只需花费很少时间.
b. 获取渠道号¶
遍历AndroidManifest.xml文件,取出渠道号所在行.
python实现如下:
with open(manifest,'r+') as m:
umeng_line = [ line.strip() for line in m.readlines() if 'UMENG_CHANNEL' in line ]
#取出渠道号所在行后,进行分割、去除.
或者直接输入编译后的apk文件夹,具体如下:
def get_apk_umeng_value(reverse_folder):
umeng_channel = []
for rfn in reverse_folder:
manifest = os.path.join(version_catalogue,rfn,'AndroidManifest.xml')
with open(manifest,'r+') as m:
umeng_line = [ line.strip() for line in m.readlines() if 'UMENG_CHANNEL' in line ]
for ul in umeng_line:
ucv = ul.split('=')[2]
#使用strip过滤"/>//--等特殊字符
umeng_channel.append(ucv.strip('"/>// --'))
return umeng_channel
完整代码地址:
Apk签名校验¶
对Apk签名进行校验,是发版前必不可少的工作。
keytool.exe -printcert -v -file CERT.RSA
输出结果如下:
Owner: CN=wang, OU=xinhuan, O=xinhuan, L=beijing, ST=beijing, C=cn
Issuer: CN=wang, OU=xinhuan, O=xinhuan, L=beijing, ST=beijing, C=cn
Serial number: 469d1fe7
Valid from: Tue Oct 27 19:38:09 CST 2015 until: Sat Oct 20 19:38:09 CST 2040
Certificate fingerprints:
MD5: D9:0B:82:E7:D6:22:81:84:AE:A0:52:44:25:E0:61:B3
SHA1: 16:D1:C5:4A:EE:04:DA:13:5C:67:F1:2E:42:7C:B5:BB:10:7D:D6:B4
SHA256: 94:2B:75:83:57:DC:3C:BB:38:FE:B4:66:98:B2:12:CE:D9:31:92:BB:EA:B2:19:9D:58:74:FD:34:CC:D5:3D:7A
Signature algorithm name: SHA256withRSA
Version: 3
Extensions:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 92 74 E2 63 9A BA 38 9C 7F A1 44 8C E5 D3 CA BC .t.c..8...D.....
0010: 79 FB 03 AF y...
]
]
第三章: Selenium手册¶
第四章: Appium手册¶
由于Python语言友好易用简洁,因此采用Appium_Python_Client作为客户端。

Appium介绍¶
Appium 是一个自动化测试开源工具,支持 iOS 平台和 Android 平台上的原生应用,web 应用和混合应用。
所谓的“移动原生应用”是指那些用 iOS 或者 Android SDK 写的应用。 所谓的“移动 web 应用”是指使用移动浏览器访问的应用(Appium 支持 iOS 上的 Safari 和 Android 上的 Chrome)。 所谓的“混合应用”是指原生代码封装网页视图 —— 原生代码和 web 内容交互。 比如,像 [Phonegap](http://phonegap.com/),可以帮助开发者使用网页技术写应用,然后用原生代码封装,这些就是混合应用。
重要的是,Appium 是一个跨平台的工具:它允许测试人员使用同样的接口,基于不同的平台(iOS, Android)写自动化测试脚本。 这样大大增加了 iOS 和 Android 测试套件间代码的复用性。
想知道 Appium 如何支持平台,版本和自动化形态的详细信息,请参见[platform support doc](platform-support.cn.md).
Appium 的理念¶
为了满足移动自动化需求, Appium 遵循着某种理念。这种理念重点体现于以下 4 个需求:
- 你无需为了自动化,而重新编译或者修改你的应用。
- 你不必局限于某种语言或者框架来写和运行测试脚本。
- 一个移动自动化的框架不应该在接口上重复造轮子。(移动自动化的接口应该统一)
- 无论是精神上,还是名义上,都必须开源。
Appium 设计¶
那么 Appium 架构是如何实现这个哲学呢?为了满足第一条,Appium 真正的工作引擎其实是第三方自动化框架。 这样,我们就不需在你的应用里植入 Appium 特定或者第三方的代码。这就意味着你在测试你将发布的应用。我们 使用以下的第三方框架:
- iOS: 苹果的UIAutomation: https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/
- Android 4.2+: Google’s UiAutomator: http://developer.android.com/tools/help/uiautomator/index.html
为了满足第二点,我们把这些第三方框架封装成一套 API,WebDriver: http://docs.seleniumhq.org/projects/webdriver/ WebDriver (也就是 “Selenium WebDriver”) 指定了客户端到服务端的协议。 (参见JSON Wire Protocol: https://w3c.github.io/webdriver/webdriver-spec.html)。 使用这种客户端-服务端的架构,我们可以使用任何语言来编写客户端,向服务端发送恰当的 HTTP 请求。 而且目前已经有大多数流行语言版本的客户端实现了。这也意味着你可以使用任何测试套件或者测试框架。客户端库就是简单的 HTTP 客户,可以以任何你喜欢的方式潜入你的代码。换句话说,Appium 和 WebDriver 客户端不是技术意义上的“测试框架”, 而是“自动化库”。你可以在你的测试环境中随意使用这些自动化库!
事实上 WebDriver 已经成为 web 浏览器自动化的标准,也成了 W3C 的标准 —— https://dvcs.w3.org/hg/webdriver/raw-file/tip/webdriver-spec.html 我们又何必为移动做一个完全不同的呢? 所以我们扩充了WebDriver 的协议:https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md, 在原有的基础上添加移动自动化相关的 API 方法,这也也满足了第三条理念。
第四条就不用说了,Appium 是开源的,https://github.com/appium/appium
Appium 概念¶
C/S 架构 Appium 的核心是一个 web 服务器,它提供了一套 REST 的接口。它收到客户端的连接,监听到命令, 接着在移动设备上执行这些命令,然后将执行结果放在 HTTP 响应中返还给客户端。事实上,这种客户端/服务端的 架构打开了许多可能性:比如我们可以使用任何实现了客户端的语言来写我们的测试代码。比如我们可以把服务端放在不同 的机器上。
Session 自动化总是在一个 session 的上下文中运行。客户端初始化一个和服务端交互的 session。不同的语言有不同的实现方式, 但是最终都是发送一个附有 “desired capabilities” 的 JSON 对象参数的 POST 请求 “/session” 给服务器。 这时候,服务端就会开始一个自动化的 session,然后返回一个 session ID。 客户端拿到这个 ID,之后就用这个 ID 发送后续的命令。
Desired Capabilities Desired capabilities 是一些键值对的集合 (比如,一个 map 或者 hash)。 客户端讲这些键值对发给服务端,告诉服务端我们想要启动怎样的自动化session。 根据不同的 capabilities 参数,服务端会有不同的行为。比如,我们可以把 platformName capability 设置为 iOS,告诉 Appium 服务端,我们想要一个 iOS 的 session,而不是一个 Android 的。我们也可以设置 safariAllowPopups capability 为 true, 确保在 Safari 自动化 session 中,我们可以使用 javascript 来打开新窗口。
Appium Server Appium server 是用 nodejs 写的。我们可以用源码编译或者从 NPM 直接安装。
Appium 服务端
Appium 服务端有很多语言库 Java, Ruby, Python, PHP, JavaScript 和 C#,这些库都实现了 Appium 对 WebDriver 协议的扩展。当使用 Appium 的时候,你只需使用这些库代替常规的 WebDriver 库就可以了。
Appium.app, Appium.exe
我们提供了 GUI 封装的 Appium server 下载。它封装了运行 Appium server 的所有依赖。 所以你需要担心 Node。而且这个封装包含了一个 Inspector 工具,可以让使用者检查应用的界面层级。 这样写测试用例的时候就非常方便了。
.Appium GUI下载地址: https://bitbucket.org/appium/appium.app/downloads/
本章引用:https://github.com/appium/appium/blob/master/docs/old/cn/intro.cn.md
Appium安装配置¶
下载¶
https://bitbucket.org/appium/appium.app/downloads/
Windows Download: 下载exe可执行文件进行安装
Mac Download: 下载dmg进行安装
Mac安装¶
> brew install node # get node.js
> npm install -g appium # get appium
> npm install wd # get appium client
> appium & # start appium
> node your-appium-test.js
Appium Andorid环境¶
- Android ADK API >= 17
- Java
Appium ios 环境¶
- Mac OS X 10.10以上版本
- Xcode 6.0以上版本
- Apple Developer Tools
Appium Python Client¶
Appium Python Client封装了标准的selenium客户端类库, 为用户提供常见的selenium命令以及额外移动设备控制相关的命令.
使用pip命令进行安装Appium Python Client
pip install Appium-Python-Client
Python Api具体见第三章节
用法¶
from appium import webdriver
from selenium import webdriver
例子
from appium import webdriver
#脚本运行需要增加下列环境参数
config = {
'platformName' = 'Android',
'platformVersion' = '6.0',
'devicesName' = 'Android Emulator',
'app' = '$PATH',
'automationName' = 'Appium'
}
driver = webdriver.Remote('http://localhost:4723/wd/hub',config)
使用Android sdk 自带的adb命令获取devicesName
adb devices -l
Appium Python Driver Api¶
使用 Appium Python Client进行测试。https://github.com/appium/python-client
python查看appium webdriver help.
>>> from appium import webdriver
>>> help(webdriver)
PACKAGE CONTENTS
common (package)
connectiontype
errorhandler
mobilecommand
switch_to
webdriver
webelement
Api: 元素定位¶
id定位 | driver.find_element_by_id(element) |
xpath定位 | driver.find_element_by_xpath(element) |
Api: 应用安装卸载¶
安装应用 | driver.install_app(‘/path/xxxxxx.apk’) |
检查应用是否安装 | driver.is_app_installed(“packagename”) |
卸载应用 | driver.remove_app(‘packagename.apk’) |
Api: 应用操作相关¶
启动应用 | driver.launch_app() |
关闭应用 | driver.close_app() |
重置应用 | driver.reset() |
把当前应用放到app后台 | driver.background_app(10) |
打开通知栏 | driver.open_notifications() |
打开通知栏功能只有Android可用
Api: 文件操作相关¶
从设备拉出文件 | driver.pull_file(‘path/filename’) |
向设备推送文件 | driver.push_file(path,data.encode(‘base64’)) |
截图 | driver.get_screenshot_as_file(‘login.png’) |
Api: 屏幕、手势操作相关¶
锁定屏幕 | drvier.lock(10) |
滑动屏幕 | driver.swipe(75,500,75,0,0.8) |
捏 | driver.pinch(element=e1) |
屏幕放大 | driver.zoom(element=e1) |
长按 | long_press() |
短按 | press() |
点击 | tap() |
移动到 | move_to() |
执行手势操作 | perform() |
释放操作 | release() |
等待 | wait() |
TouchAction:触摸操作
from appium.webdriver.common.touch_action import TouchAction
# 录制小视频的时候,长按录制10s
action = TouchAction(driver)
action.long_press(element,None,None,10000).release().perform()
Api: 键盘操作相关¶
在ios上收起键盘 | driver.hide_keyboard() | |
发送键盘事件 | driver.keyevent(176) |
摇晃设备 | driver.shake() |
Api: 应用上下文¶
列出所有的可用上下文 | driver.contexts |
列出当前上下文 | driver.current_context |
将上下文切换到默认上 | driver.switch_to.context(None) |
应用的字符串 | driver.app_strings |
Api: Activity相关¶
获得activity | driver.current_activity |
Appium 键盘事件¶
语法:
driver.keyevent(keyCode)
1.键盘重要键¶
回车键(Enter) 66
空格键(Space) 62
Tab键(Tab) 61
退格键(Backspace) 67
2.手机实体键¶
Home键 3
返回键 4
电话键 5
结束通话键 6
音量上下 24,25
电源键 26
相机键 27
静音键 164
搜索键 84
菜单键 82
清除键 28
3.字符键¶
数字0-9 7-16
字母a-z 29-54
星号 17
井号键 18
等号 70
[] 71,72
\ 73
; 74
' 75
/ 76
@ 77
+ 81
- 69
` 68
. 55
, 56
上下左右中间 19-23
Android元素定位¶
uiautomatiorviewer¶
uiautomatorviewer是Android SDK自带的工具,在$ANDROID_HOME/tools目录下。
使用的测试App为:旧爱,在各大应用市场都可以搜索到。
点击uiautomatorviewer.bat后,将鼠标置于元素之上,如下:

使用resource_id定位¶
driver.find_element_by_id("com.jiuai:id/rbPersonal").click()
ios元素定位¶
Android屏幕滑动¶
滑动,就是一个点移动另一个点。
用法:
def swipe(self, start_x, start_y, end_x, end_y, duration=None):
"""Swipe from one point to another point, for an optional duration.
:Args:
- start_x - x-coordinate at which to start
- start_y - y-coordinate at which to start
- end_x - x-coordinate at which to stop
- end_y - y-coordinate at which to stop
- duration - (optional) time to take the swipe, in ms.
:Usage:
driver.swipe(100, 100, 100, 400)
"""
屏幕左滑¶
driver.swipe(height/4*3,width/2,height/4,width/2,800)
屏幕右滑¶
driver.swipe(height/4,width/2,height/4*3,width/2,800)
屏幕下滑¶
driver.swipe(height/2,width/4,height/2,width/4*3,800)
屏幕上滑¶
driver.swipe(height/2,width/4*3,height/2,width/4,800)
Appium使用断言¶
在编写Appium脚本中,可借用Python unittest断言。
比如判断activity:
assertEqual(".activity.MainActivity",driver.current_activity)
解决中文输入问题¶
问题¶
使用Appium进行Android测试时,使用send_keys()发送中文,输入框没有输入任何文本
解决办法¶
使用Appium键盘,appium执行时,会在Android手机安装一个特殊键盘(即Appium Android lnput Manager for Unicode)
在Appum config中增加下列代码:
'unicodeKeyboard':True,
'resetKeyboard':True
解释:
- 使用unicode的编码方式发送字符
- Unicode键盘并非虚拟键盘,在界面上不会显示出来
说明¶
Android tests allow for Unicode input by installing and using a specialized keyboard
that allows the text to be passed as ASCII text between Appium and the application
being tested.
In order to utilize this functionality, set the unicodeKeyboard desired capability is
set to true. If the keyboard should be returned to its original state, the resetKeyboard
desired capability should also be set to true. Otherwise Appium’s Unicode keyboard will
remain enabled on the device after the tests are completed.
Then tests can pass Unicode text to editable fields using send_keys
Appium截图¶
driver.get_screenshot_as_file('screenshot/go_home.png')
扩展:提高脚本复用、可配置性¶
问题¶
在编写Appium脚本过程中,某个元素的resource_id或class可能在多个文件被使用, 当界面发生变化的时候,脚本将变得难以维护
解决办法¶
使用configparser提高Appium脚本的复用性、可配置性 将element全部写到一个配置文件中,比如config.ini或config.cfg
比如config.ini配置文件如下:
;登录
[login]
user = com.jiuai:id/et_phoneNumber
pwd = com.jiuai:id/et_password
loginbtn = com.jiuai:id/btn_common_login
forgetpwd = com.jiuai:id/tv_forget_pwd
userment = com.jiuai:id/tv_user_agreement
tvregister = com.jiuai:id/tv_register
使用Python configparser读取配置文件¶
from configparser import ConfigParser
# read config.ini
cfg = configParser()
cfg.read('config.ini')
读取其中值:
cfg.get('login','user')
实际操作:
driver.find_element_by_id(cfg.get('login','user')).click()