之前做分享写的文档,备档~
0.移动客户端自动化简介
客户端自动化测试的本质
定位对象 · 操作对象 · 校验对象
对象的定位应该是自动化测试的核心,要想操作、校验一个对象,首先应该识别这个对象。
一个对象具有特定的属性,譬如:文字、类型、位置等等。我们可以通过这些属性找到这对象。
移动客户端自动化测试常见框架
Android
- Monkey
- Monkeyrunner
- UIautomator
- Robotium
iOS
- UIAutomation
通用
- Appium
- Calabash...
框架选择
- 支持平台,系统版本
- 是否需要源码
- 脚本语言
- 是否支持webview...
浅谈移动端自动化测试框架和工具
iOS自动化测试框架对比
1.Appium 思想·原理·环境搭建
Appium 思想
架构原理
- 左边的WebDriver script是我们的selenium测试脚本
- 中间是起的Appium的服务,Appium在这边起了一个Server,Appium Server接收webdriver 标准请求,解析请求内容,调⽤用对应的框架响应操作。如:脚本发送一个点击按钮的请求给appium server
- Appium server会把请求转发给中间件Bootstrap.jar ,它是用java写的,安装在手机上.Bootstrap 接收appium 的命令,最终通过调⽤用UiAutomator的命令来实现
- 最后执⾏行的结果由Bootstrap返回给Appium server
Appium环境搭建python篇(mac系统)
安装时使用VPN可能比淘宝源成功率更高~
2.定位对象 · 操作对象 · 校验对象
定位对象-Android
工具
UiAutomatorview (4.1下版本不支持)
启动方法
➜ ~ uiautomatorviewer
定位方法
查找客户端页面元素方法
以下是Appium给出的所有方法(但不一定支持!)
查找页面中存在的单个元素 | 查找页面中存在多个元素(返回数组) | 查找方法 |
---|---|---|
find_element | find_elements | def find_element(self, by=By.ID, value=None) 被其他find_element方法调用 |
find_element_by_accessibility_id | find_elements_by_accessibility_id | Accessibility ID在Android上面就等同于contentDescription,这个属性是方便一些生理功能有缺陷的人使用应用程序的。 UiAutomatorview中content-desc |
find_element_by_android_uiautomator | find_elements_by_android_uiautomator | 使用android_uiautomator表达 |
find_element_by_class_name | find_elements_by_class_name | UiAutomatorview中class |
find_element_by_id | find_elements_by_id | UiAutomatorview中resource-id |
find_element_by_ios_uiautomation | find_elements_by_ios_uiautomation | - |
find_element_by_link_text | find_elements_by_link_text | UiAutomatorview中text |
find_element_by_name | find_elements_by_name | Appium 1.5 版本已经被废除 |
find_element_by_partial_link_text | find_elements_by_partial_link_text | UiAutomatorview中text,可以匹配部分文字 譬如:美食(XX家) |
find_element_by_tag_name | find_elements_by_tag_name | Appium 1.0 版本已经被废除 |
find_element_by_xpath | find_elements_by_xpath | xpath,强烈不推荐 |
find_element_by_android_uiautomator() 举栗子
def autotest_find_name_by_uiautomator(self, name): return self.driver.find_element_by_android_uiautomator('new UiSelector().text("%s")' % name)
find_element_by_android_uiautomator()中的字符串为Uiautomator的写法,主要有以下几种方法:
new UiSelector().text("%s")new UiSelector().textContains("%s")new UiSelector().className("%s")new UiSelector().resourceId("%s")
textContains类似于find_element_by_partial_link_text,可以做部分文字匹配
操作对象
以下是常用的方法,更多的方法可以通过源码或提示获得
tips:某些方法可能在特定系统不支持
# 点击对象.click() # 文本框输入文字.send_keys("%s")#获取控件文字.text # 从(startx,starty)滑到(endx,endy),分duration步滑,每一步用时是5毫秒。driver.swipe(int startx, int starty, int endx, int endy, int duration) # 按手机固定键,key值定义在AndroidKeyCode类中# keycode http://blog.csdn.net/crisschan/article/details/50419963driver.send_key_event(int key) # 获取当前的activitydriver.current_activity() # 等待指定的activity出现,interval为扫描间隔1秒driver.wait_activity(activity, timeout, interval=1) ...
校验对象
在Python环境下,Appium 官方推荐使用unittest来维护测试用例,unittest提供了多种assert方法,我们可以通过这些方法来做校验。
unittest中常用的assert语句
校验方式
- 校验控件是否展示
- 校验控件中的数据(文本)是否正确
- 图片对比
- ...
代码实现
校验元素非空
def assert_element_exist_by_x(self, how, what, msg=None): """ 通过*方法验证元素非空 :param how:find_element_by_* methods (By.CLASS_NAME/By.TAG_NAME/By.NAME/By.PARTIAL_LINK_TEXT/By.NAME/By.XPATH/By.ID) :param what: :param msg: """ try: if how ==By.NAME: self.assertIsNotNone(self.autotest_find_name_by_uiautomator(what), msg) else: self.assertIsNotNone(self.driver.find_element(by=how, value=what), msg) return True except Exception: print "xxxxx"
3.用例编写
官方栗子
import osimport unittestfrom appium import webdriverfrom time import sleep# Returns abs path relative to this file and not cwdPATH = lambda p: os.path.abspath( os.path.join(os.path.dirname(__file__), p))class ContactsAndroidTests(unittest.TestCase): def setUp(self): desired_caps = {} desired_caps['platformName'] = 'Android' desired_caps['platformVersion'] = '4.2' desired_caps['deviceName'] = 'Android Emulator' desired_caps['app'] = PATH( '../../../sample-code/apps/ContactManager/ContactManager.apk' ) desired_caps['appPackage'] = 'com.example.android.contactmanager' desired_caps['appActivity'] = '.ContactManager' self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps) def tearDown(self): self.driver.quit() def test_add_contacts(self): el = self.driver.find_element_by_accessibility_id("Add Contact") el.click() textfields = self.driver.find_elements_by_class_name("android.widget.EditText") textfields[0].send_keys("Appium User") textfields[2].send_keys("someone@appium.io") self.assertEqual('Appium User', textfields[0].text) self.assertEqual('someone@appium.io', textfields[2].text) self.driver.find_element_by_accessibility_id("Save").click() # for some reason "save" breaks things alert = self.driver.switch_to_alert() # no way to handle alerts in Android self.driver.find_element_by_android_uiautomator('new UiSelector().clickable(true)').click() self.driver.press_keycode(3)if __name__ == '__main__': suite = unittest.TestLoader().loadTestsFromTestCase(ContactsAndroidTests) unittest.TextTestRunner(verbosity=2).run(suite)
setup
在Appium中所有动作的前提是要启动一个session,启动方法就是
self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
其中desired_caps是配置信息,它告诉server 本次测试的具体内容,譬如:
- 本次测试是启动浏览器还是启动移动设备?
- 是启动andorid还是启动ios?
- 启动android时,app的package是什么?
- 启动android时,app的activity是什么?
必要的desired_caps参数
desired_caps['platformName'] = 'Android'desired_caps['platformVersion'] = '4.2'desired_caps['deviceName'] = '022MWW146T007732'desired_caps['app'] = 'XXX.apk'
- platformName:使用哪种移动平台。iOS, Android, orFirefoxOS?
- deviceName:启动哪种设备,是真机还是模拟器?iPhone Simulator, iPad Simulator, iPhone Retina 4-inch, Android Emulator, Galaxy S4, etc...
- app:应用的绝对路径,注意一定是绝对路径。如果指定了appPackage和appActivity的话,这个属性是可以不设置的。另外这个属性和browserName属性是冲突的。
- automationName:使用哪种自动化引擎。appium(默认)还是Selendroid?
- appActivity:待测试的app的Activity名字。比如MainActivity, .Settings。注意,原生app的话要在activity前加个"."。
- appPackage:待测试的app的java package。比如com.example.android.myApp, com.android.settings。
teardown
self.driver.quit()
4.其他
- Android 获取当前Activity、包名方法adb shell dumpsys activity | grep "mFocusedActivity"adb shell pm list package
- 元素定位不到/没有id的方法麻烦开发增加特有、不重复的id