51Testing软件测试论坛
标题: [基础问题]Appium-移动端自动测试框架,如何入门? [打印本页]
作者: lsekfe 时间: 2017-8-24 11:42
标题: [基础问题]Appium-移动端自动测试框架,如何入门?
做自动化测试研究,发现了Appium这个工具,但看遍资料,却还无法入门,自己在windows下跑android程序,下载windows版本Appium后,点击launch后,下一步操作为何?如何与手机建立连接?自动化脚本需要怎么执行?浏览器中打开localhost没有写脚本的地方啊。
作者: lsekfe 时间: 2017-8-24 11:45
作者:九毫
链接:https://www.zhihu.com/question/21453695/answer/103719176
来源:知乎
-------------------------------------------------------------
背景
最近新加入DJI的某项目组(以下均已M指代),需要从零开始搭建功能自动化测试平台。
简单地说,M是一个典型的移动互联网产品,客户端包括iOS和Android,并在app中通过WebView嵌入了H5,后端基于Ruby on Rails实现。
当前阶段,M项目除了Rails Server端采用Jenkins+RSpec实现了部分的持续集成功能外,客户端部分的部署和测试工作都还是完全依赖于手工操作。
基于当前项目的开发模式,我对整个M项目实现持续集成自动化测试的架构流程进行了规划,初步计划的架构图如下图所示。最终的目标是希望能实现:不管是Rails Server,还是App(iOS/Android),以及H5,当任意部分存在代码提交时,系统能自动拉取最新代码进行部署并执行自动化回归测试,及时地将执行情况反馈给开发人员。
[attach]108428[/attach]
目标确定后,便是分阶段进行实现,需要开发的模块包括:
- 自动化测试平台(Automated Test Platform):满足iOS/Android/H5的自动化功能测试,包括模拟器和真机的测试;
- 测试管理平台(Test Management Platform):实现自动化测试用例管理、手动下发测试任务、测试结果报表展现、Dashboard等功能;
- 打包平台(Pack System):实现iOS/Android的自动化构建;
- 服务端自动化测试(Rails):将服务端Rails的自动化测试接入测试管理平台;
- 持续集成流程打通:对Jenkins进行二次开发,与测试管理平台打通,实现全流程的持续集成自动化测试。
而本系列教程,《从0到1搭建移动App功能自动化测试平台》,便是对整个实践过程的一个记录。
需要说明的是,之前我个人的工作经历主要在服务端性能测试、Android客户端性能测试(测试开发)方向,对于客户端的自动化测试基本上没有经验积累,特别是iOS系统的测试,以前更是完全没有接触过。因此本系列教程只能算是个人在探索路上的学习总结和记录,可能会存在一些错误的观点,还请前辈们多多指教。
自动化测试框架的选择
在愿景图中,绿色方框(Automated Test Platform)负责移动应用客户端(iOS/Android/H5)自动化测试的调度和执行,是整个自动化测试平台的核心。
因此,在搭建自动化测试平台之前,首先需要选择一个合适的自动化测试框架。
对于移动应用的自动化测试框架,当前市面上已经有很多成熟的开源项目。针对当前项目的实际情况,我主要参考如下选择标准:
- 同时支持iOS、Android、H5,且尽量能保持接口统一,减少开发维护成本;
- 编程语言支持Python/Ruby;
- 用户量大,文档丰富。
经过筛选,Appium无疑是最佳的选择。
Appium简介
对于Appium的详细介绍,大家可参考Appium官方文档,我就不再重复引用。
不过对于Appium,仍然有几点很赞的理念值得强调。
- 采用Appium时,无需对被测应用做任何修改,也无需嵌入任何东西(SDK);
- Appium对iOS和Android的原生自动化测试框架进行了封装,并提供了统一的API(WebDriver API),减少了自动化测试代码的维护工作量;
- Appium采用Client-Server的架构设计,并采用标准的HTTP通信协议;Server端负责与iOS/Android原生测试框架交互,无需测试人员关注细节实现;Client端基本上可以采用任意主流编程语言编写测试用例,减少了学习成本。
环境准备(iOS)
在Appium中测试iOS时,依赖于Apple开发环境,因此,在运行Appium之前需要先确保如下环境安装正确:
- Mac OS X >= 10.7
- XCode >= 4.6.3
- Apple Developer Tools (iPhone simulator SDK, command line tools)
如上几个环境安装比较简单,直接在Apple Store中安装即可。
在安装Appium之前,为了确保Appium的相关依赖已经准备就绪,可以使用appium-doctor来进行验证。
appium-doctor是一个用于验证appium安装环境的工具,可以诊断出Node/iOS/Android环境配置方面的常见问题。
appium-doctor采用node.js编写,采用npm即可在Terminal中进行安装:
$ npm install appium-doctor -g
安装完毕后,执行appium-doctor命令即可对Appium的环境依赖情况进行检测;指定--ios时只针对iOS环境配置进行检测,指定--android参数时只针对Android环境配置进行检测,若不指定则同时对iOS和Android环境进行检测。
$ appium-doctor --ios
info AppiumDoctor ### Diagnostic starting ###
info AppiumDoctor ✔ Xcode is installed at: /Applications/Xcode.app/Contents/Developer
info AppiumDoctor ✔ Xcode Command Line Tools are installed.
info AppiumDoctor ✔ DevToolsSecurity is enabled.
info AppiumDoctor ✔ The Authorization DB is set up properly.
info AppiumDoctor ✔ The Node.js binary was found at: /usr/local/bin/node
info AppiumDoctor ✔ HOME is set to: /Users/Leo
info AppiumDoctor ### Diagnostic completed, no fix needed. ###
info AppiumDoctor
info AppiumDoctor Everything looks good, bye!
info AppiumDoctor
若检测结果全部通过,则说明Appium的相关依赖已经准备就绪,接下来可以继续安装Appium。
安装Appium
根据前面的介绍,Appium采用Client-Server的架构设计,因此安装Appium时需要分别安装Server部分和Client部分。
通常情况下,我们说的Appium都是指代的Server部分。Appium的安装有多种方式:可以通过源码编译安装,也可以在Terminal中通过npm命令安装,另一种是直接下载appium.dmg后安装应用程序。
在这里推荐运行Appium app的方式,除了GUI界面操作更直观以外,更重要的一个原因是,相比于命令行运行方式,Appium app多了一个Inspector模块,可以调用模拟器运行被测应用程序,并且可以很方便地在预览页面中查看UI元素的层级结构和详细控件属性,极大地提高编写测试脚本的效率。
至于Client部分,其实我们原本可以不安装任何东西,只需要任意选择一门开发语言,然后直接基于WebDriver的C/S协议(JSON Wire Protocol)即可编写自动化测试代码。但是这样做的话工作量会比较大,因为要去处理一些跟协议相关的工作。所幸Appium项目已经针对众多主流的编程语言,将底层协议处理相关的工作封装为Library,通过调用这些Library,可以极大地简化我们编写测试用例的工作量。
而说的需要安装的Client部分,其实也就是安装这些Library。选定编写测试用例的语言后,我们就可以针对性地进行安装。
例如,如果选择Ruby语言,那么需要安装的Library就是appium_lib,安装方式如下:
$ gem install appium_lib
如果选择Python语言,那么需要安装的Library就是Appium-Python-Client,安装方式如下:
$ pip install Appium-Python-Client
对于其它编程语言,请自行参考官方文档。
To be continued ...
iOS的自动化测试环境已基本准备就绪了,接下来我们想做的第一件事,就是在模拟器中运行iOS应用。
在下一篇文章中,我们将从clone项目源码为起点,编译生成iOS app,在Appium中调用模拟器中运行iOS app,并分享实践过程中遇到的一些坑。
作者: lsekfe 时间: 2017-8-24 11:49
Appium——驱动和常用功能的封装背景初步了解Appium各个功能之后,应该把这些功能进行一些封装,否则整个代码会比较难看,可用性和重用性也会很差。本文是我这段时间使用Appium的一些想法,仅供参考。
操作系统:Mac OS X EI Caption
Appium: 1.4.16
Java: java version “1.7.0_79”
node.js: v5.3.0
npm: 3.3.12
手机:小米NOTE4
待测应用: 微证券
driver的封装初始化的driver是Python操作Appium的核心,因此driver在整个代码中重用率是非常高的。
新建一个driver.py文件,专门用来封装driver。代码如下:
- # ecoding=utf-8
- __author__ = "Sven_Weng"
- from appium import webdriver
- class AppiumTest:
- def __init__(self):
- desired_caps = {'platformName': 'Android',
- 'platformVersion': '5.0.2',
- 'deviceName': '5136b01e',
- 'appPackage': 'com.weizq',
- 'appActivity': 'com.zztzt.android.simple.app.MainActivity'}
- self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
- self.driver.implicitly_wait(30)
- def get_driver(self):
- return self.driver
复制代码在AppiumTest这个类中,初始化函数包含了driver的信息,然后在get_driver函数中直接把这个driver返回回去,测试用例中只要在测试类的初始化中调用它,就能获取driver。
按模块划分封装的方法我是这么划分模块的,一个Element,专门封装操作和对象有关的方法,把find_element_by_id这种很长很长的方法缩短一点,用一些比较简洁的名字封装一下,使用起来代码可读性也会比较强。一个common类,专门封装通用的方法,其他就是按照功能模块来划分,我测试的微证券有4个主要功能,所以会有财讯、行情、发现、我这些类模块。
element对象封装我自己的代码如下:
- class Element:
- """
- 封装Appium中关于元素对象的方法
- """
- def __init__(self):
- at = AppiumTest()
- self.driver = at.get_driver()
- def get_id(self, id):
- element = self.driver.find_element_by_id(id)
- return element
- def get_name(self, name):
- element = self.driver.find_element_by_name(name)
- return element
- def over(self):
- element = self.driver.quit()
- return element
- def get_screen(self, path):
- self.driver.get_screenshot_as_file(path)
- def get_size(self):
- size = self.driver.get_window_size()
- return size
- def swipe_to_up(self):
- window_size = self.get_size()
- width = window_size.get("width")
- height = window_size.get("height")
- self.driver.swipe(width / 2, height * 3 / 4, width / 2, height / 4, 500)
- def swipe_to_down(self):
- window_size = self.get_size()
- width = window_size.get("width")
- height = window_size.get("height")
- self.driver.swipe(width / 2, height / 4, width / 2, height * 3 / 4, 500)
- def swipe_to_left(self):
- window_size = self.get_size()
- width = window_size.get("width")
- height = window_size.get("height")
- self.driver.swipe(width / 4, height / 2, width * 3 / 4, height / 2, 500)
- def swipe_to_right(self):
- window_size = self.get_size()
- width = window_size.get("width")
- height = window_size.get("height")
- self.driver.swipe(width * 4 / 5, height / 2, width / 5, height / 2, 500)
- def back(self):
- self.driver.keyevent(4)
- def get_classes(self, classesname):
- elements = self.driver.find_elements_by_class_name(classesname)
- return elements
- def get_ids(self, ids):
- elements = self.driver.find_elements_by_id(ids)
- return elements
- def switch_h5(self):
- self.driver.execute(MobileCommand.SWITCH_TO_CONTEXT, {"name": "WEBVIEW_com.weizq"})
- def switch_app(self):
- self.driver.execute(MobileCommand.SWITCH_TO_CONTEXT, {"name": "NATIVE_APP"})
复制代码上面封装的很简单,就是一些常用的方法,在初始化函数中初始化driver即可。
commoncommon就是封装一些通用的方法,比如登录,比大小,获取股票代码,页面标题校验等等。以下是我的代码:
- class Common:
- """
- 封装通用的方法
- """
- def __init__(self):
- self.cf = ConfigParser.ConfigParser()
- self.cf.read('enable_data.conf')
- def cycle_screen(self, section_name, num, driver):
- """
- 循环截图功能
- 从json文件中读取信息,用来定位点击的方位和保存截图的路径
- 执行成功则返回True,否则返回False
- :param section_name: json文件中一级节点的名称
- :param num: json文件中对应的列表序列位
- :param driver: Appium的驱动
- :return:True or False
- """
- try:
- with open('get_screen.json', 'r') as f:
- jsondata = f.read()
- cx_info = json.loads(jsondata)
- values = cx_info[section_name][num].values()
- for value in values:
- driver.get_name(value['name']).click()
- time.sleep(2)
- self.check_title(value['name'], driver)
- driver.get_screen(value['path'])
- logging.info(value['log'])
- return True
- except Exception as e:
- logging.warning(e)
- return False
- def cycle_screen_and_back(self, section_name, num, driver):
- """
- 功能和上一个函数一样,在步骤最后加了一个返回事件,用来处理"行情"-"更多"页面的数据校验
- :param section_name: conf配置文件的节点名称
- """
- try:
- with open('get_screen.json', 'r') as f:
- jsondata = f.read()
- cx_info = json.loads(jsondata)
- values = cx_info[section_name][num].values()
- for value in values:
- logging.info(value)
- driver.get_name(value['name']).click()
- time.sleep(2)
- self.check_title(value['name'], driver)
- # title = driver.get_classes('android.widget.TextView')[0].text
- # try:
- # assert title == value['name']
- # logging.info(title + '页面显示正确')
- # except Exception as e:
- # logging.warning(value['name'])
- # logging.warning(title + '页面显示不正确')
- driver.get_screen(value['path'])
- driver.driver.keyevent(4)
- time.sleep(1)
- logging.info(value['log'])
- return True
- except Exception as e:
- logging.warning(e)
- return False
- def check_title(self, title, driver):
- text = driver.get_classes('android.widget.TextView')[0].text
- try:
- assert text == title
- logging.info("标题为: {} 校验通过".format(title))
- return True
- except Exception as e:
- logging.warning(e)
- logging.warning("标题为: {} 校验不通过".format(title))
- return False
- def compare(self, ids, numstar, numend, driver):
- """
- 比较传入值得大小, 第一个数比第二个数大返回True,否则返回False
- :param ids:传入的ID名
- :param numstar:传入的开始序号
- :param numend:传入的结束需要
- :param driver:驱动
- :return:True or False
- """
- try:
- startext = float(driver.get_ids(ids)[numstar].text)
- endtext = float(driver.get_ids(ids)[numend].text)
- except ValueError:
- starmark = driver.get_ids(ids)[numstar].text[:1]
- endmark = driver.get_ids(ids)[numend].text[:1]
- if starmark == endmark:
- startext = float(driver.get_ids(ids)[numstar].text.lstrip(starmark).rstrip('%'))
- endtext = float(driver.get_ids(ids)[numend].text.lstrip(endmark).rstrip('%'))
- elif starmark == "+":
- return True
- elif starmark == "-":
- return False
- if startext > endtext:
- return True
- else:
- return False
- def get_SotckCode(self, ids, driver):
- """
- 获取行情-自选页面中的所有股票代码,自选超过50支的时候请修改参数中的range(10)
- 返回list
- :param driver:驱动控件
- :return:list, 自选股票代码
- """
- SotckCode = []
- for x in driver.get_ids(ids):
- SotckCode.append(x.text)
- for x in range(5):
- driver.swipe_to_up()
- for x in driver.get_ids(ids):
- if x.text not in SotckCode:
- SotckCode.append(x.text)
- return SotckCode
- def zjzh_login(self, driver):
- """
- 资金账号登录方法,传入appium-driver
- APP启动时需要资金账号为登录状态时调用
- :param driver: Appium驱动
- :return: True
- """
- driver.get_name('发现').click()
- driver.switch_h5()
- driver.get_classes('list-item')[1].click()
- driver.switch_app()
- driver.get_name('买入').click()
- username = self.cf.get('zjzh_login_info', 'username')
- password = self.cf.get('zjzh_login_info', 'password')
- if self.tra_login(username, password, driver):
- self.check_title(u'委托买入', driver)
- driver.back()
- driver.back()
- self.check_title(u'发现', driver)
- logging.info('资金账号登录成功')
- return True
- def tra_login(self, username, password, driver):
- """
- 资金账号登录页面的登录方法
- :param username:账号
- :param password:密码
- :param driver:Appium驱动
- :return:True
- """
- driver.get_id('com.weizq:id/edit_account').clear()
- driver.get_id('com.weizq:id/edit_account').send_keys(username)
- logging.info('输入的账号为: {}'.format(username))
- driver.get_id('com.weizq:id/edit_password').send_keys(password)
- logging.info('输入的密码为: {}'.format(password))
- yzm = driver.get_id('com.weizq:id/text_yanzhengma').text
- logging.info('验证码为:{}'.format(yzm))
- driver.get_id('com.weizq:id/edit_yanzhengma').send_keys(yzm)
- driver.get_id('com.weizq:id/login').click()
- time.sleep(3)
- return True
复制代码 其他功能模块其他功能模块也是同样的道理,不过其他模块封装的都是特定功能模块才会用到的方法,因为代码可能涉及公司的机密,所以功能模块的代码就暂不放出来,思想还是一样的。
日志记录关于日志记录的原则,我觉得是这样的,写入日志的必须是一些关键的信息,把日志浏览一遍,基本上就能了解到整个脚本跑的流程,比如我调试一些功能的时候,基本上不用关注手机运行的页面,脚本到底做了什么,只要看日志就懂了。下面是一些我调试功能的时候打出来的日志:
- Thu, 24 Mar 2016 14:26:35 test_weizq.py[line:437] INFO ==========结束测试我的内容==========
- Thu, 24 Mar 2016 14:26:37 test_weizq.py[line:429] INFO ==========开始测试我的内容==========
- Thu, 24 Mar 2016 14:26:55 test_weizq.py[line:429] INFO ==========开始测试我的内容==========
- Thu, 24 Mar 2016 14:27:23 test_weizq.py[line:429] INFO ==========开始测试我的内容==========
- Thu, 24 Mar 2016 14:27:32 test_weizq.py[line:448] INFO -----开始测试个人资料页面校验-----
- Thu, 24 Mar 2016 14:27:36 test_weizq.py[line:456] INFO 旧的昵称是: 6850252
- Thu, 24 Mar 2016 14:27:50 test_weizq.py[line:463] INFO 新的昵称是: 5030013
- Thu, 24 Mar 2016 14:27:50 test_weizq.py[line:437] INFO ==========结束测试我的内容==========
- Thu, 24 Mar 2016 14:31:55 test_weizq.py[line:429] INFO ==========开始测试我的内容==========
- Thu, 24 Mar 2016 14:32:06 test_weizq.py[line:448] INFO -----开始测试个人资料页面校验-----
- Thu, 24 Mar 2016 14:32:09 test_weizq.py[line:468] INFO 女
- Thu, 24 Mar 2016 14:32:09 test_weizq.py[line:437] INFO ==========结束测试我的内容==========
- Thu, 24 Mar 2016 14:34:38 test_weizq.py[line:429] INFO ==========开始测试我的内容==========
- Thu, 24 Mar 2016 14:34:50 test_weizq.py[line:448] INFO -----开始测试个人资料页面校验-----
- Thu, 24 Mar 2016 14:34:53 test_weizq.py[line:468] INFO 修改前的性别: 女
- Thu, 24 Mar 2016 14:34:58 test_weizq.py[line:472] INFO 性别修改为: 男
- Thu, 24 Mar 2016 14:34:58 test_weizq.py[line:437] INFO ==========结束测试我的内容==========
- Thu, 24 Mar 2016 14:35:14 test_weizq.py[line:429] INFO ==========开始测试我的内容==========
- Thu, 24 Mar 2016 14:35:35 test_weizq.py[line:429] INFO ==========开始测试我的内容==========
- Thu, 24 Mar 2016 14:35:47 test_weizq.py[line:429] INFO ==========开始测试我的内容==========
- Thu, 24 Mar 2016 14:36:01 test_weizq.py[line:429] INFO ==========开始测试我的内容==========
- Thu, 24 Mar 2016 14:36:12 test_weizq.py[line:448] INFO -----开始测试个人资料页面校验-----
- Thu, 24 Mar 2016 14:36:16 test_weizq.py[line:456] INFO 旧的昵称是: 5030013
- Thu, 24 Mar 2016 14:36:28 test_weizq.py[line:463] INFO 新的昵称是: 908602
- Thu, 24 Mar 2016 14:36:28 test_weizq.py[line:468] INFO 修改前的性别: 男
- Thu, 24 Mar 2016 14:36:33 test_weizq.py[line:475] INFO 性别修改为: 女
- Thu, 24 Mar 2016 14:36:33 test_weizq.py[line:437] INFO ==========结束测试我的内容==========
复制代码基本上看到这些日志就知道我到底在测试什么东西了。
作者:点点寒彬
作者: lsekfe 时间: 2017-8-24 11:56
Appium——处理混合APP中H5的操作景自己自学APPIUM测试公司的APP有一段时间的,Android原生的元素定位,包括数据处理和逻辑判断也基本上搞熟了,但是公司的APP很坑爹,开发过程中存在混合开发的情况,这就意味着我必须要处理APP里面的webview,真的是很坑爹的东西。
操作系统:Mac OS X EI Caption
Appium: 1.4.16
Java: java version "1.7.0_79"
node.js: v5.3.0
npm: 3.3.12
手机:小米NOTE4
待测应用: 微证券
常规方法正常来说,常规的方法应该是使用UI Automator Viewer来探测页面的元素,比如下面这样:
[attach]108429[/attach]
但是,如果是混合开发,就会出现只有一个webview元素的情况,正常的方法无法定位。比如这样:
[attach]108430[/attach]
常规的方法是没办法定位出这部分内容,只有一个webview,无法像原生一样获取内容。
取巧的办法如果针对H5的元素,只需要点击的话,可以这么做,比如上文我们的开户页面,我们需要做的只是在这个页面上点击立即开户,跳转到下一个页面,我们可以用这种取巧的方法:
- self.driver.get_name('立即开户').click()
复制代码这样的代码,也是可以实现的,当然,上面的get_name我是进行封装了的,原来的代码是:
- def get_name(self, name):
- element = self.driver.find_element_by_name(name)
- return element
复制代码 更优化的处理方式既然是H5页面,如果能够获取HTML代码就好了,那么就可以使用操作DOM的方式来操作这个H5。
谷歌浏览器:版本 49.0.2623.87 (64-bit)
我用的谷歌浏览器是这个版本,把手机连接上电脑,在谷歌浏览器中输入这个代码:
点击这个按钮:
[attach]108431[/attach]
神奇的一幕出现了,所有的代码都展示在你面前。就像web一样调试混合APP中的H5.
切换webdriver虽然解决了html代码的问题,但是另一个问题出现了,我们要怎么去操作DOM。常规的方法是没办法了,只能操作原生的,这个时候我们需要把webdriver切换成H5的,而不是原生的。
其实原理很简单,只要把webdriver切换到H5的方式就行了。使用如下代码:
- contexts
- contexts(self):
- Returns the contexts within the current session.
- 返回当前会话中的上下文,使用后可以识别H5页面的控件
- :Usage:
- driver.contexts
- 用法 driver.contexts
复制代码调用这个方法后,把结果打印出来,你会发现是一个列表,列表中的第一个是NATIVE_APP,这个就表示现在的webdriver是调用原生的功能,我们使用这个命令切换一下就行了。
- def switch_h5(self):
- self.driver.execute(MobileCommand.SWITCH_TO_CONTEXT, {"name": "WEBVIEW_com.weizq"})
- def switch_app(self):
- self.driver.execute(MobileCommand.SWITCH_TO_CONTEXT, {"name": "NATIVE_APP"})
复制代码第一个函数就是封装切换到H5的方法,name对应的东西就是通过contexts列表中打印出来的东西,当然你也可以使用其他的方法封装。
切换到H5的webdriver之后,再使用driver.find_element_by_id试试?现在就变成查找DOM相关的功能了,再调用第二个函数,又切回原生的webdriver了。
作者:点点寒彬
作者: lsekfe 时间: 2017-8-24 12:01
Appium--无安卓源码的一些准备准备工作其实是比较简单了,现阶段我测试的是安卓,所以就需要准备一台安卓手机了,在环境准备中已经把电脑的环境变量配置完毕了,所以现在需要的就是一些安卓测试的一些前奏知识。
环境
操作系统:Mac OS X EI Caption
Appium: 1.4.16
Java: java version "1.7.0_79"
node.js: v5.3.0
npm: 3.3.12
一切的前提,需要配置好环境
获取包名我这里假设是我们都没有源码的情况下,有源码当然比较简单,如果只有一个apk文件,我们要获取包名的方式也一样很简单。在终端中输入下列命令:
- adb logcat ActivityManager:I *:s
复制代码然后打开需要测试的APP,找到topActivity.packageName,里面就是待测应用的包名了。
[attach]108432[/attach]
这条命令的实质就是Android手机的日志打印之后,通过管道符过滤,LInux也同样有效,Windows可能就没办法使用这条命令了,当然,windows的朋友可以使用管道符之前的命令,然后找到topActivity.packageName。也同样可以获取到包名。
当前Activity同样是无源码,也可以通过命令来获取当前Activity。
- Mac/Linux: adb shell dumpsys window windows | grep mFocusedApp
- Windows: adb shell dump sys window windows 并且手动查找mFocusedApp行
复制代码效果如下图:
[attach]108433[/attach]
获取页面元素Appium和Selenium一样,需要通过一些元素来定位,达到自动化测试的目的,这里我使用的是Android Studio里面的一个工具:Android Device Monitor。
这个工具可以获取屏幕快照,然后点击相关元素就可以获得上面的信息了,如下图所示:
[attach]108434[/attach]
最后熟悉使用这些东西,我们就可以开始Appium之旅了。
作者:点点寒彬
作者: lsekfe 时间: 2017-8-24 13:35
Appium环境搭建Appium学习之路—环境搭建说实话,Appium第一次接触还是在testerhome看到的,介绍说这个框架可以测试安卓和IOS,并且只要写一个脚本,不需要做任何改动就可以直接在IOS和Android上运行,这真心是一个好消息,说干就干。先吧环境弄起来看看到底牛掰不牛掰。 PS:笔者使用的是Mac OS X系统。
准备工作Appium提供了两种方式,一个是客户端,另一个是命令行。不过不论哪种方式,以下的东西都必须准备好Xcode Command Line Tools 这个是必须的工具可以百度以下,我是使用Mac系统,干脆直接下了一个完整的Xcode Android SDK 和AVD 这个是测试Android必须的,当然AVD可以使用Android的真机来代替。
Appium客户端我看到这个框架竟然还是有客户端的,真是有一种莫名的兴奋,有客户端就意味着我可以不用搭建那些操蛋的命令行了?下载一个GUI端我就可以轻轻松松的开搞了。不过下载客户端是需要翻墙的,这个有点蛋疼,我自己的专门翻墙出去下载了一个Appium for Mac的客户端,不过那速度慢得让人无法直视。官网地址:Appium官网。我是不太建议在官网下载。这里推荐一个资源,下载速度比较快。Appium国内下载地址。
客户端的使用我个人折腾完客户端之后,感觉不是很给力,客户端虽然界面挺漂亮的,风格也挺好,不过总体折腾下来不是我喜欢的类型,如果喜欢客户端的朋友可以自己去折腾折腾客户端,配置起来很简单,就不做过多的赘述了。
命令行命令行是体现逼格高大上的东东,不过折腾起来也很费事,笔者折腾这个命令行折腾了大概一周的时间,其中种种原因很多
Mac自带了这东西,不过说实话一般的电脑也都会把这个环境折腾好吧,毕竟java是最流行的开发语言
Mac自带了这东西,因为笔者是使用Python来写脚本,当然你也可以使用其他语言来写,比如java、ruby、c#等等
brew是一个套件管理器,不过笔者比较懒,直接使用Mac自带的easy_install来安装,比较省事
node也是必须的,如果有了brew或者easy_install,安装是很省事的 sudo easy_install node,输入密码就搞定了
- SvenWengdeMBP:~ svenweng$ node -v v0.10.34
复制代码这样就算安装成功
npm这东西和node一样,也是使用安装工具来安装很方便
- SvenWengdeMBP:~ svenweng$ npm -v 1.4.28
复制代码终于到我们的主角了安装命令是 npm install -g appium 这里要说明一下,Appium的安装不能使用sudo。-g是让npm自动帮我们配置Appium的环境。
- SvenWengdeMBP:~ svenweng$ appium -v 1.4.16
复制代码
如果你看到了这个东西,就表示你的Appium已经完全安装完毕了。网上还有教程说需要安装一个wd的东西,我不知道这个是干什么的,当然我也安装了,npm install wd。很简单的安装
检查Appium的配置是否正确执行命令appium-doctor,你如果看到一下的界面,就表示已经正确的安装完毕了
- SvenWengdeMBP:~ svenweng$ appium-doctor Running iOS Checks ✔ Xcode is installed at /Applications/Xcode.app/Contents/Developer ✔ Xcode Command Line Tools are installed. ✔ DevToolsSecurity is enabled. ✔ The Authorization DB is set up properly. ✔ Node binary found at /usr/local/bin/node ✔ iOS Checks were successful.
- Running Android Checks ✔ ANDROID_HOME is set to "/Users/svenweng/Desktop/Application/adt-bundle-mac-x86_64-20131030/sdk" ✔ JAVA_HOME is set to "/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home." ✔ ADB exists at /Users/svenweng/Desktop/Application/adt-bundle-mac-x86_64-20131030/sdk/platform-tools/adb ✔ Android exists at /Users/svenweng/Desktop/Application/adt-bundle-mac-x86_64-20131030/sdk/tools/android ✔ Emulator exists at /Users/svenweng/Desktop/Application/adt-bundle-mac-x86_64-20131030/sdk/tools/emulator ✔ Android Checks were successful.
- ✔ All Checks were successful
复制代码如果某一行签名不是打钩,而是一个红叉叉,那就表示那个配置没有安装正确
疑问理论上我的Appium安装是正确的,但是我运行appium的时候出现了下面的一大串错误
- SvenWengdeMBP:~ svenweng$ appium error: uncaughtException: fn must be a function
- > See http://goo.gl/916lJJ
- date=Sat Nov 21 2015 10:37:25 GMT+0800 (HKT), pid=2504, uid=501, gid=20, cwd=/usr/local/lib/node_modules/appium, execPath=/usr/local/bin/node, version=v0.10.34, argv=[node, /usr/local/bin/appium], rss=103559168, heapTotal=86062080, heapUsed=56309664, loadavg=[1.6328125, 1.86767578125, 1.81103515625], uptime=39552, trace=[column=15, file=/usr/local/lib/node_modules/appium/node_modules/appium-chromedriver/node_modules/appium-jsonwp-proxy/node_modules/appium-support/node_modules/bluebird/js/main/promisify.js, function=Function.Promise.promisify, line=268, method=Promise.promisify, native=false, column=13, file=lib/fs.js, function=, line=46, method=null, native=false, column=26, file=module.js, function=Module._compile, line=456, method=_compile, native=false, column=10, file=module.js, function=Object.Module._extensions..js, line=474, method=Module._extensions..js, native=false, column=32, file=module.js, function=Module.load, line=356, method=load, native=false, column=12, file=module.js, function=Function.Module._load, line=312, method=Module._load, native=false, column=17, file=module.js, function=Module.require, line=364, method=require, native=false, column=17, file=module.js, function=require, line=380, method=null, native=false, column=11, file=/usr/local/lib/node_modules/appium/node_modules/appium-chromedriver/node_modules/appium-jsonwp-proxy/node_modules/appium-support/build/lib/tempdir.js, function=, line=12, method=null, native=false, column=26, file=module.js, function=Module._compile, line=456, method=_compile, native=false, column=10, file=module.js, function=Object.Module._extensions..js, line=474, method=Module._extensions..js, native=false, column=32, file=module.js, function=Module.load, line=356, method=load, native=false, column=12, file=module.js, function=Function.Module._load, line=312, method=Module._load, native=false, column=17, file=module.js, function=Module.require, line=364, method=require, native=false, column=17, file=module.js, function=require, line=380, method=null, native=false, column=19, file=/usr/local/lib/node_modules/appium/node_modules/appium-chromedriver/node_modules/appium-jsonwp-proxy/node_modules/appium-support/build/index.js, function=, line=11, method=null, native=false, column=26, file=module.js, function=Module._compile, line=456, method=_compile, native=false, column=10, file=module.js, function=Object.Module._extensions..js, line=474, method=Module._extensions..js, native=false, column=32, file=module.js, function=Module.load, line=356, method=load, native=false, column=12, file=module.js, function=Function.Module._load, line=312, method=Module._load, native=false, column=17, file=module.js, function=Module.require, line=364, method=require, native=false, column=17, file=module.js, function=require, line=380, method=null, native=false, column=42, file=lib/proxy.js, function=, line=2, method=null, native=false, column=26, file=module.js, function=Module._compile, line=456, method=_compile, native=false, column=10, file=module.js, function=Object.Module._extensions..js, line=474, method=Module._extensions..js, native=false, column=32, file=module.js, function=Module.load, line=356, method=load, native=false, column=12, file=module.js, function=Function.Module._load, line=312, method=Module._load, native=false, column=17, file=module.js, function=Module.require, line=364, method=require, native=false, column=17, file=module.js, function=require, line=380, method=null, native=false, column=17, file=/usr/local/lib/node_modules/appium/node_modules/appium-chromedriver/node_modules/appium-jsonwp-proxy/build/index.js, function=, line=9, method=null, native=false, column=26, file=module.js, function=Module._compile, line=456, method=_compile, native=false, column=10, file=module.js, function=Object.Module._extensions..js, line=474, method=Module._extensions..js, native=false, column=32, file=module.js, function=Module.load, line=356, method=load, native=false, column=12, file=module.js, function=Function.Module._load, line=312, method=Module._load, native=false, column=17, file=module.js, function=Module.require, line=364, method=require, native=false, column=17, file=module.js, function=require, line=380, method=null, native=false, column=28, file=lib/chromedriver.js, function=, line=3, method=null, native=false, column=26, file=module.js, function=Module._compile, line=456, method=_compile, native=false, column=10, file=module.js, function=Object.Module._extensions..js, line=474, method=Module._extensions..js, native=false, column=32, file=module.js, function=Module.load, line=356, method=load, native=false], stack=[TypeError: fn must be a function, , See http://goo.gl/916lJJ, , at Function.Promise.promisify (/usr/local/lib/node_modules/appium/node_modules/appium-chromedriver/node_modules/appium-jsonwp-proxy/node_modules/appium-support/node_modules/bluebird/js/main/promisify.js:268:15), at Object.[HTML_REMOVED] (lib/fs.js:46:13), at Module._compile (module.js:456:26), at Object.Module._extensions..js (module.js:474:10), at Module.load (module.js:356:32), at Function.Module._load (module.js:312:12), at Module.require (module.js:364:17), at require (module.js:380:17), at Object.[HTML_REMOVED] (/usr/local/lib/node_modules/appium/node_modules/appium-chromedriver/node_modules/appium-jsonwp-proxy/node_modules/appium-support/build/lib/tempdir.js:12:11), at Module._compile (module.js:456:26), at Object.Module._extensions..js (module.js:474:10), at Module.load (module.js:356:32), at Function.Module._load (module.js:312:12), at Module.require (module.js:364:17), at require (module.js:380:17), at Object.[HTML_REMOVED] (/usr/local/lib/node_modules/appium/node_modules/appium-chromedriver/node_modules/appium-jsonwp-proxy/node_modules/appium-support/build/index.js:11:19), at Module._compile (module.js:456:26), at Object.Module._extensions..js (module.js:474:10), at Module.load (module.js:356:32), at Function.Module._load (module.js:312:12), at Module.require (module.js:364:17), at require (module.js:380:17), at Object.[HTML_REMOVED] (lib/proxy.js:2:42), at Module._compile (module.js:456:26), at Object.Module._extensions..js (module.js:474:10), at Module.load (module.js:356:32), at Function.Module._load (module.js:312:12), at Module.require (module.js:364:17), at require (module.js:380:17), at Object.[HTML_REMOVED] (/usr/local/lib/node_modules/appium/node_modules/appium-chromedriver/node_modules/appium-jsonwp-proxy/build/index.js:9:17), at Module._compile (module.js:456:26), at Object.Module._extensions..js (module.js:474:10), at Module.load (module.js:356:32), at Function.Module._load (module.js:312:12), at Module.require (module.js:364:17), at require (module.js:380:17), at Object.[HTML_REMOVED] (lib/chromedriver.js:3:28), at Module._compile (module.js:456:26), at Object.Module._extensions..js (module.js:474:10), at Module.load (module.js:356:32)]
复制代码求大神给一个解答吧。
翻墙工具正常了,我进入了命令行提示的错误网址 网址上显示的是这个 Error: fn must be a function
This page is here for the sake of completeness. There really is nothing much to add on that error. 翻译过来就是: 错误: FN必须是一个函数
这页是这里为了完整性。真的是没有什么补充该错误。
折腾了一天,终于把问题搞定了,问题的原因是因为node的版本太旧了导致的,原因分析在1楼回答的那个链接上,再说说今天自己折腾过程中遇到的问题吧,我在网上搜索帖子的时候说需要吧node卸载了才能升级(我真是天真)。于是按照步骤手贱删除了一些不应该删除的东西,结果搞的很乱,思路都乱了,不知道从何下手。于是我就去睡了一觉,醒来重新折腾,重新去官网下了一个node的安装包,把之前干掉的node环境重新部署好。再使用npm install -g appium的时候,一直会出现这个问题
- SvenWengdeMBP:~ svenweng$ npm install -g appium npm ERR! tar.unpack unzip error /var/folders/hm/04tcnt812v909nysd8jgl0100000gn/T/npm-10484-941ff227/registry.npmjs.org/appium-uiauto/-/appium-uiauto-1.10.10.tgz npm ERR! tar.unpack unzip error /var/folders/hm/04tcnt812v909nysd8jgl0100000gn/T/npm-10484-941ff227/registry.npmjs.org/appium-instruments/-/appium-instruments-2.0.6.tgz loadRequestedDeps → netwo ▐ ╢███████████████████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░╟ ^C SvenWengdeMBP:~ svenweng$ sudo chmod -r 777 /usr/local Password: chmod: 777: No such file or directory SvenWengdeMBP:~ svenweng$ sudo chmod -R 777 /usr/local SvenWengdeMBP:~ svenweng$ npm install -g appium loadRequestedDeps → netwo ▄ ╢███████████████████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░╟ ^C SvenWengdeMBP:~ svenweng$ npm install -g appium npm ERR! tar.unpack unzip error /var/folders/hm/04tcnt812v909nysd8jgl0100000gn/T/npm-13599-e762bf07/registry.npmjs.org/hoek/-/hoek-2.16.3.tgz npm ERR! tar.unpack untar error /var/folders/hm/04tcnt812v909nysd8jgl0100000gn/T/npm-13599-e762bf07/registry.npmjs.org/hoek/-/hoek-2.16.3.tgz npm ERR! tar.unpack unzip error /var/folders/hm/04tcnt812v909nysd8jgl0100000gn/T/npm-13599-e762bf07/registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.8.tgz npm ERR! tar.unpack untar error /var/folders/hm/04tcnt812v909nysd8jgl0100000gn/T/npm-13599-e762bf07/registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.8.tgz npm ERR! tar.unpack unzip error /var/folders/hm/04tcnt812v909nysd8jgl0100000gn/T/npm-13599-e762bf07/registry.npmjs.org/core-js/-/core-js-0.9.18.tgz npm ERR! tar.unpack unzip error /var/folders/hm/04tcnt812v909nysd8jgl0100000gn/T/npm-13599-e762bf07/registry.npmjs.org/appium-adb/-/appium-adb-1.7.5.tgz npm ERR! tar.unpack unzip error /var/folders/hm/04tcnt812v909nysd8jgl0100000gn/T/npm-13599-e762bf07/registry.npmjs.org/appium-instruments/-/appium-instruments-2.0.6.tgz npm ERR! tar.unpack unzip error /var/folders/hm/04tcnt812v909nysd8jgl0100000gn/T/npm-13599-e762bf07/registry.npmjs.org/appium-uiauto/-/appium-uiauto-1.10.10.tgz npm ERR! Darwin 15.0.0 npm ERR! argv "/usr/local/bin/node" "/usr/local/bin/npm" "install" "-g" "appium" npm ERR! node v5.1.0 npm ERR! npm v3.3.12 npm ERR! code Z_BUF_ERROR npm ERR! errno -5
- npm ERR! unexpected end of file npm ERR! npm ERR! If you need help, you may report this error at: npm ERR! https://github.com/npm/npm/issues
- npm ERR! Please include the following file with any support request: npm ERR! /Users/svenweng/npm-debug.log
复制代码每次安装到一半都安装不下去,我的电脑也是翻墙的,按道理应该也都能下载才对,具体详细的原因请恕我是小白,不太懂这个。不过我换了一个下载源就把这个问题搞定了,这里发出来,希望下次有遇到这种问题的朋友不会在这个地方浪费太多时间。命令是这个:npm -g --registry http://registry.cnpmjs.org install appium
前前后后折腾了差不多一周的环境,终于把环境的问题搞定了,希望能够更深入的学习,也给自己一个勉励。
作者:点点寒彬
欢迎光临 51Testing软件测试论坛 (http://bbs.51testing.com/) |
Powered by Discuz! X3.2 |