51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 3852|回复: 5
打印 上一主题 下一主题

[Appium] [基础问题]Appium-移动端自动测试框架,如何入门?

[复制链接]
  • TA的每日心情
    擦汗
    昨天 09:02
  • 签到天数: 1046 天

    连续签到: 4 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2017-8-24 11:42:55 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    做自动化测试研究,发现了Appium这个工具,但看遍资料,却还无法入门,自己在windows下跑android程序,下载windows版本Appium后,点击launch后,下一步操作为何?如何与手机建立连接?自动化脚本需要怎么执行?浏览器中打开localhost没有写脚本的地方啊。
    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

  • TA的每日心情
    擦汗
    昨天 09:02
  • 签到天数: 1046 天

    连续签到: 4 天

    [LV.10]测试总司令

    2#
     楼主| 发表于 2017-8-24 11:45:30 | 只看该作者
    作者:九毫
    链接: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,当任意部分存在代码提交时,系统能自动拉取最新代码进行部署并执行自动化回归测试,及时地将执行情况反馈给开发人员。


    目标确定后,便是分阶段进行实现,需要开发的模块包括:

    • 自动化测试平台(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,并分享实践过程中遇到的一些坑。





    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    昨天 09:02
  • 签到天数: 1046 天

    连续签到: 4 天

    [LV.10]测试总司令

    3#
     楼主| 发表于 2017-8-24 11:49:22 | 只看该作者
    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。代码如下:

    1. # ecoding=utf-8
    2. __author__ = "Sven_Weng"
    3. from appium import webdriver


    4. class AppiumTest:
    5.     def __init__(self):
    6.         desired_caps = {'platformName': 'Android',
    7.                         'platformVersion': '5.0.2',
    8.                         'deviceName': '5136b01e',
    9.                         'appPackage': 'com.weizq',
    10.                         'appActivity': 'com.zztzt.android.simple.app.MainActivity'}
    11.         self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
    12.         self.driver.implicitly_wait(30)

    13.     def get_driver(self):
    14.         return self.driver
    复制代码

    在AppiumTest这个类中,初始化函数包含了driver的信息,然后在get_driver函数中直接把这个driver返回回去,测试用例中只要在测试类的初始化中调用它,就能获取driver。

    按模块划分封装的方法

    我是这么划分模块的,一个Element,专门封装操作和对象有关的方法,把find_element_by_id这种很长很长的方法缩短一点,用一些比较简洁的名字封装一下,使用起来代码可读性也会比较强。一个common类,专门封装通用的方法,其他就是按照功能模块来划分,我测试的微证券有4个主要功能,所以会有财讯、行情、发现、我这些类模块。

    element对象封装

    我自己的代码如下:

    1. class Element:
    2.     """
    3.     封装Appium中关于元素对象的方法
    4.     """

    5.     def __init__(self):
    6.         at = AppiumTest()
    7.         self.driver = at.get_driver()

    8.     def get_id(self, id):
    9.         element = self.driver.find_element_by_id(id)
    10.         return element

    11.     def get_name(self, name):
    12.         element = self.driver.find_element_by_name(name)
    13.         return element

    14.     def over(self):
    15.         element = self.driver.quit()
    16.         return element

    17.     def get_screen(self, path):
    18.         self.driver.get_screenshot_as_file(path)

    19.     def get_size(self):
    20.         size = self.driver.get_window_size()
    21.         return size

    22.     def swipe_to_up(self):
    23.         window_size = self.get_size()
    24.         width = window_size.get("width")
    25.         height = window_size.get("height")
    26.         self.driver.swipe(width / 2, height * 3 / 4, width / 2, height / 4, 500)

    27.     def swipe_to_down(self):
    28.         window_size = self.get_size()
    29.         width = window_size.get("width")
    30.         height = window_size.get("height")
    31.         self.driver.swipe(width / 2, height / 4, width / 2, height * 3 / 4, 500)

    32.     def swipe_to_left(self):
    33.         window_size = self.get_size()
    34.         width = window_size.get("width")
    35.         height = window_size.get("height")
    36.         self.driver.swipe(width / 4, height / 2, width * 3 / 4, height / 2, 500)

    37.     def swipe_to_right(self):
    38.         window_size = self.get_size()
    39.         width = window_size.get("width")
    40.         height = window_size.get("height")
    41.         self.driver.swipe(width * 4 / 5, height / 2, width / 5, height / 2, 500)

    42.     def back(self):
    43.         self.driver.keyevent(4)

    44.     def get_classes(self, classesname):
    45.         elements = self.driver.find_elements_by_class_name(classesname)
    46.         return elements

    47.     def get_ids(self, ids):
    48.         elements = self.driver.find_elements_by_id(ids)
    49.         return elements

    50.     def switch_h5(self):
    51.         self.driver.execute(MobileCommand.SWITCH_TO_CONTEXT, {"name": "WEBVIEW_com.weizq"})

    52.     def switch_app(self):
    53.         self.driver.execute(MobileCommand.SWITCH_TO_CONTEXT, {"name": "NATIVE_APP"})
    复制代码

    上面封装的很简单,就是一些常用的方法,在初始化函数中初始化driver即可。

    common

    common就是封装一些通用的方法,比如登录,比大小,获取股票代码,页面标题校验等等。以下是我的代码:

    1. class Common:
    2.     """
    3.     封装通用的方法
    4.     """

    5.     def __init__(self):
    6.         self.cf = ConfigParser.ConfigParser()
    7.         self.cf.read('enable_data.conf')

    8.     def cycle_screen(self, section_name, num, driver):
    9.         """
    10.         循环截图功能
    11.         从json文件中读取信息,用来定位点击的方位和保存截图的路径
    12.         执行成功则返回True,否则返回False
    13.         :param section_name: json文件中一级节点的名称
    14.         :param num: json文件中对应的列表序列位
    15.         :param driver: Appium的驱动
    16.         :return:True or False
    17.         """
    18.         try:
    19.             with open('get_screen.json', 'r') as f:
    20.                 jsondata = f.read()
    21.             cx_info = json.loads(jsondata)
    22.             values = cx_info[section_name][num].values()
    23.             for value in values:
    24.                 driver.get_name(value['name']).click()
    25.                 time.sleep(2)
    26.                 self.check_title(value['name'], driver)
    27.                 driver.get_screen(value['path'])
    28.                 logging.info(value['log'])
    29.             return True
    30.         except Exception as e:
    31.             logging.warning(e)
    32.             return False

    33.     def cycle_screen_and_back(self, section_name, num, driver):
    34.         """
    35.         功能和上一个函数一样,在步骤最后加了一个返回事件,用来处理"行情"-"更多"页面的数据校验
    36.         :param section_name: conf配置文件的节点名称
    37.         """
    38.         try:
    39.             with open('get_screen.json', 'r') as f:
    40.                 jsondata = f.read()
    41.             cx_info = json.loads(jsondata)
    42.             values = cx_info[section_name][num].values()
    43.             for value in values:
    44.                 logging.info(value)
    45.                 driver.get_name(value['name']).click()
    46.                 time.sleep(2)
    47.                 self.check_title(value['name'], driver)
    48.                 # title = driver.get_classes('android.widget.TextView')[0].text
    49.                 # try:
    50.                 #     assert title == value['name']
    51.                 #     logging.info(title + '页面显示正确')
    52.                 # except Exception as e:
    53.                 #     logging.warning(value['name'])
    54.                 #     logging.warning(title + '页面显示不正确')
    55.                 driver.get_screen(value['path'])
    56.                 driver.driver.keyevent(4)
    57.                 time.sleep(1)
    58.                 logging.info(value['log'])
    59.             return True
    60.         except Exception as e:
    61.             logging.warning(e)
    62.             return False

    63.     def check_title(self, title, driver):
    64.         text = driver.get_classes('android.widget.TextView')[0].text
    65.         try:
    66.             assert text == title
    67.             logging.info("标题为: {} 校验通过".format(title))
    68.             return True
    69.         except Exception as e:
    70.             logging.warning(e)
    71.             logging.warning("标题为: {} 校验不通过".format(title))
    72.             return False

    73.     def compare(self, ids, numstar, numend, driver):
    74.         """
    75.         比较传入值得大小, 第一个数比第二个数大返回True,否则返回False
    76.         :param ids:传入的ID名
    77.         :param numstar:传入的开始序号
    78.         :param numend:传入的结束需要
    79.         :param driver:驱动
    80.         :return:True or False
    81.         """
    82.         try:
    83.             startext = float(driver.get_ids(ids)[numstar].text)
    84.             endtext = float(driver.get_ids(ids)[numend].text)
    85.         except ValueError:
    86.             starmark = driver.get_ids(ids)[numstar].text[:1]
    87.             endmark = driver.get_ids(ids)[numend].text[:1]
    88.             if starmark == endmark:
    89.                 startext = float(driver.get_ids(ids)[numstar].text.lstrip(starmark).rstrip('%'))
    90.                 endtext = float(driver.get_ids(ids)[numend].text.lstrip(endmark).rstrip('%'))
    91.             elif starmark == "+":
    92.                 return True
    93.             elif starmark == "-":
    94.                 return False
    95.         if startext > endtext:
    96.             return True
    97.         else:
    98.             return False

    99.     def get_SotckCode(self, ids, driver):
    100.         """
    101.         获取行情-自选页面中的所有股票代码,自选超过50支的时候请修改参数中的range(10)
    102.         返回list
    103.         :param driver:驱动控件
    104.         :return:list, 自选股票代码
    105.         """
    106.         SotckCode = []
    107.         for x in driver.get_ids(ids):
    108.             SotckCode.append(x.text)
    109.         for x in range(5):
    110.             driver.swipe_to_up()
    111.             for x in driver.get_ids(ids):
    112.                 if x.text not in SotckCode:
    113.                     SotckCode.append(x.text)
    114.         return SotckCode

    115.     def zjzh_login(self, driver):
    116.         """
    117.         资金账号登录方法,传入appium-driver
    118.         APP启动时需要资金账号为登录状态时调用
    119.         :param driver: Appium驱动
    120.         :return: True
    121.         """
    122.         driver.get_name('发现').click()
    123.         driver.switch_h5()
    124.         driver.get_classes('list-item')[1].click()
    125.         driver.switch_app()
    126.         driver.get_name('买入').click()
    127.         username = self.cf.get('zjzh_login_info', 'username')
    128.         password = self.cf.get('zjzh_login_info', 'password')
    129.         if self.tra_login(username, password, driver):
    130.             self.check_title(u'委托买入', driver)
    131.             driver.back()
    132.             driver.back()
    133.             self.check_title(u'发现', driver)
    134.             logging.info('资金账号登录成功')
    135.             return True

    136.     def tra_login(self, username, password, driver):
    137.         """
    138.         资金账号登录页面的登录方法
    139.         :param username:账号
    140.         :param password:密码
    141.         :param driver:Appium驱动
    142.         :return:True
    143.         """
    144.         driver.get_id('com.weizq:id/edit_account').clear()
    145.         driver.get_id('com.weizq:id/edit_account').send_keys(username)
    146.         logging.info('输入的账号为: {}'.format(username))
    147.         driver.get_id('com.weizq:id/edit_password').send_keys(password)
    148.         logging.info('输入的密码为: {}'.format(password))
    149.         yzm = driver.get_id('com.weizq:id/text_yanzhengma').text
    150.         logging.info('验证码为:{}'.format(yzm))
    151.         driver.get_id('com.weizq:id/edit_yanzhengma').send_keys(yzm)
    152.         driver.get_id('com.weizq:id/login').click()
    153.         time.sleep(3)
    154.         return True
    复制代码
    其他功能模块

    其他功能模块也是同样的道理,不过其他模块封装的都是特定功能模块才会用到的方法,因为代码可能涉及公司的机密,所以功能模块的代码就暂不放出来,思想还是一样的。

    日志记录

    关于日志记录的原则,我觉得是这样的,写入日志的必须是一些关键的信息,把日志浏览一遍,基本上就能了解到整个脚本跑的流程,比如我调试一些功能的时候,基本上不用关注手机运行的页面,脚本到底做了什么,只要看日志就懂了。下面是一些我调试功能的时候打出来的日志:

    1. Thu, 24 Mar 2016 14:26:35 test_weizq.py[line:437] INFO ==========结束测试我的内容==========

    2. Thu, 24 Mar 2016 14:26:37 test_weizq.py[line:429] INFO ==========开始测试我的内容==========

    3. Thu, 24 Mar 2016 14:26:55 test_weizq.py[line:429] INFO ==========开始测试我的内容==========

    4. Thu, 24 Mar 2016 14:27:23 test_weizq.py[line:429] INFO ==========开始测试我的内容==========

    5. Thu, 24 Mar 2016 14:27:32 test_weizq.py[line:448] INFO -----开始测试个人资料页面校验-----

    6. Thu, 24 Mar 2016 14:27:36 test_weizq.py[line:456] INFO 旧的昵称是: 6850252

    7. Thu, 24 Mar 2016 14:27:50 test_weizq.py[line:463] INFO 新的昵称是: 5030013

    8. Thu, 24 Mar 2016 14:27:50 test_weizq.py[line:437] INFO ==========结束测试我的内容==========

    9. Thu, 24 Mar 2016 14:31:55 test_weizq.py[line:429] INFO ==========开始测试我的内容==========

    10. Thu, 24 Mar 2016 14:32:06 test_weizq.py[line:448] INFO -----开始测试个人资料页面校验-----

    11. Thu, 24 Mar 2016 14:32:09 test_weizq.py[line:468] INFO 女

    12. Thu, 24 Mar 2016 14:32:09 test_weizq.py[line:437] INFO ==========结束测试我的内容==========

    13. Thu, 24 Mar 2016 14:34:38 test_weizq.py[line:429] INFO ==========开始测试我的内容==========

    14. Thu, 24 Mar 2016 14:34:50 test_weizq.py[line:448] INFO -----开始测试个人资料页面校验-----

    15. Thu, 24 Mar 2016 14:34:53 test_weizq.py[line:468] INFO 修改前的性别: 女

    16. Thu, 24 Mar 2016 14:34:58 test_weizq.py[line:472] INFO 性别修改为: 男

    17. Thu, 24 Mar 2016 14:34:58 test_weizq.py[line:437] INFO ==========结束测试我的内容==========

    18. Thu, 24 Mar 2016 14:35:14 test_weizq.py[line:429] INFO ==========开始测试我的内容==========

    19. Thu, 24 Mar 2016 14:35:35 test_weizq.py[line:429] INFO ==========开始测试我的内容==========

    20. Thu, 24 Mar 2016 14:35:47 test_weizq.py[line:429] INFO ==========开始测试我的内容==========

    21. Thu, 24 Mar 2016 14:36:01 test_weizq.py[line:429] INFO ==========开始测试我的内容==========

    22. Thu, 24 Mar 2016 14:36:12 test_weizq.py[line:448] INFO -----开始测试个人资料页面校验-----

    23. Thu, 24 Mar 2016 14:36:16 test_weizq.py[line:456] INFO 旧的昵称是: 5030013

    24. Thu, 24 Mar 2016 14:36:28 test_weizq.py[line:463] INFO 新的昵称是: 908602

    25. Thu, 24 Mar 2016 14:36:28 test_weizq.py[line:468] INFO 修改前的性别: 男

    26. Thu, 24 Mar 2016 14:36:33 test_weizq.py[line:475] INFO 性别修改为: 女

    27. Thu, 24 Mar 2016 14:36:33 test_weizq.py[line:437] INFO ==========结束测试我的内容==========
    复制代码

    基本上看到这些日志就知道我到底在测试什么东西了。

    作者:点点寒彬


    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    昨天 09:02
  • 签到天数: 1046 天

    连续签到: 4 天

    [LV.10]测试总司令

    4#
     楼主| 发表于 2017-8-24 11:56: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来探测页面的元素,比如下面这样:

    但是,如果是混合开发,就会出现只有一个webview元素的情况,正常的方法无法定位。比如这样:

    常规的方法是没办法定位出这部分内容,只有一个webview,无法像原生一样获取内容。

    取巧的办法

    如果针对H5的元素,只需要点击的话,可以这么做,比如上文我们的开户页面,我们需要做的只是在这个页面上点击立即开户,跳转到下一个页面,我们可以用这种取巧的方法:

    1. self.driver.get_name('立即开户').click()
    复制代码

    这样的代码,也是可以实现的,当然,上面的get_name我是进行封装了的,原来的代码是:

    1. def get_name(self, name):
    2.         element = self.driver.find_element_by_name(name)
    3.         return element
    复制代码
    更优化的处理方式

    既然是H5页面,如果能够获取HTML代码就好了,那么就可以使用操作DOM的方式来操作这个H5。


    谷歌浏览器:版本 49.0.2623.87 (64-bit)


    我用的谷歌浏览器是这个版本,把手机连接上电脑,在谷歌浏览器中输入这个代码:

    1. chrome://inspect
    复制代码

    点击这个按钮:

    神奇的一幕出现了,所有的代码都展示在你面前。就像web一样调试混合APP中的H5.

    切换webdriver

    虽然解决了html代码的问题,但是另一个问题出现了,我们要怎么去操作DOM。常规的方法是没办法了,只能操作原生的,这个时候我们需要把webdriver切换成H5的,而不是原生的。

    其实原理很简单,只要把webdriver切换到H5的方式就行了。使用如下代码:

    1. contexts
    2. contexts(self):

    3.     Returns the contexts within the current session.
    4.     返回当前会话中的上下文,使用后可以识别H5页面的控件

    5.     :Usage:
    6.         driver.contexts
    7. 用法 driver.contexts
    复制代码

    调用这个方法后,把结果打印出来,你会发现是一个列表,列表中的第一个是NATIVE_APP,这个就表示现在的webdriver是调用原生的功能,我们使用这个命令切换一下就行了。

    1. def switch_h5(self):
    2.         self.driver.execute(MobileCommand.SWITCH_TO_CONTEXT, {"name": "WEBVIEW_com.weizq"})

    3.     def switch_app(self):
    4.         self.driver.execute(MobileCommand.SWITCH_TO_CONTEXT, {"name": "NATIVE_APP"})
    复制代码

    第一个函数就是封装切换到H5的方法,name对应的东西就是通过contexts列表中打印出来的东西,当然你也可以使用其他的方法封装。

    切换到H5的webdriver之后,再使用driver.find_element_by_id试试?现在就变成查找DOM相关的功能了,再调用第二个函数,又切回原生的webdriver了。

    作者:点点寒彬


    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    昨天 09:02
  • 签到天数: 1046 天

    连续签到: 4 天

    [LV.10]测试总司令

    5#
     楼主| 发表于 2017-8-24 12:01:16 | 只看该作者
    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文件,我们要获取包名的方式也一样很简单。在终端中输入下列命令:

    1. adb logcat ActivityManager:I *:s
    复制代码

    然后打开需要测试的APP,找到topActivity.packageName,里面就是待测应用的包名了。

    这条命令的实质就是Android手机的日志打印之后,通过管道符过滤,LInux也同样有效,Windows可能就没办法使用这条命令了,当然,windows的朋友可以使用管道符之前的命令,然后找到topActivity.packageName。也同样可以获取到包名。

    当前Activity

    同样是无源码,也可以通过命令来获取当前Activity。

    1. Mac/Linux: adb shell dumpsys window windows | grep mFocusedApp
    2. Windows: adb shell dump sys window windows  并且手动查找mFocusedApp行
    复制代码

    效果如下图:

    获取页面元素

    Appium和Selenium一样,需要通过一些元素来定位,达到自动化测试的目的,这里我使用的是Android Studio里面的一个工具:Android Device Monitor。

    这个工具可以获取屏幕快照,然后点击相关元素就可以获得上面的信息了,如下图所示:

    最后

    熟悉使用这些东西,我们就可以开始Appium之旅了。

    作者:点点寒彬


    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    昨天 09:02
  • 签到天数: 1046 天

    连续签到: 4 天

    [LV.10]测试总司令

    6#
     楼主| 发表于 2017-8-24 13:35:25 | 只看该作者
    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国内下载地址

    客户端的使用

    我个人折腾完客户端之后,感觉不是很给力,客户端虽然界面挺漂亮的,风格也挺好,不过总体折腾下来不是我喜欢的类型,如果喜欢客户端的朋友可以自己去折腾折腾客户端,配置起来很简单,就不做过多的赘述了。

    命令行

    命令行是体现逼格高大上的东东,不过折腾起来也很费事,笔者折腾这个命令行折腾了大概一周的时间,其中种种原因很多

    • java环境

    Mac自带了这东西,不过说实话一般的电脑也都会把这个环境折腾好吧,毕竟java是最流行的开发语言

    • Python环境

    Mac自带了这东西,因为笔者是使用Python来写脚本,当然你也可以使用其他语言来写,比如java、ruby、c#等等

    • brew

    brew是一个套件管理器,不过笔者比较懒,直接使用Mac自带的easy_install来安装,比较省事

    • node

    node也是必须的,如果有了brew或者easy_install,安装是很省事的 sudo easy_install node,输入密码就搞定了

    1. SvenWengdeMBP:~ svenweng$ node -v v0.10.34
    复制代码

    这样就算安装成功

    • npm

    npm这东西和node一样,也是使用安装工具来安装很方便

    1. SvenWengdeMBP:~ svenweng$ npm -v 1.4.28
    复制代码
    • Appium

    终于到我们的主角了安装命令是 npm install -g appium 这里要说明一下,Appium的安装不能使用sudo。-g是让npm自动帮我们配置Appium的环境。

    1. SvenWengdeMBP:~ svenweng$ appium -v 1.4.16
    复制代码

    如果你看到了这个东西,就表示你的Appium已经完全安装完毕了。网上还有教程说需要安装一个wd的东西,我不知道这个是干什么的,当然我也安装了,npm install wd。很简单的安装

    检查Appium的配置是否正确

    执行命令appium-doctor,你如果看到一下的界面,就表示已经正确的安装完毕了

    1. 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.

    2. 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.

    3. ✔ All Checks were successful
    复制代码

    如果某一行签名不是打钩,而是一个红叉叉,那就表示那个配置没有安装正确

    疑问

    理论上我的Appium安装是正确的,但是我运行appium的时候出现了下面的一大串错误

    1. SvenWengdeMBP:~ svenweng$ appium error: uncaughtException: fn must be a function

    2. > See http://goo.gl/916lJJ
    3. 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的时候,一直会出现这个问题

    1. 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

    2. 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

    3. 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

    前前后后折腾了差不多一周的环境,终于把环境的问题搞定了,希望能够更深入的学习,也给自己一个勉励。

    作者:点点寒彬


    回复 支持 反对

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-15 01:08 , Processed in 0.077311 second(s), 24 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表