一、selenium、WebDriver 了解

经常听说 selenium、WebDriver 了,但他们是什么,用来干什么的呢?今天借此课程查阅一些资料,原来是这样子的。

1.1 进一步了解 WebDriver

1.2 WebDriver API

官方 API:http://www.seleniumhq.org/docs/03_webdriver.jsp#introducing-the-selenium-webdriver-api-by-example
中文 API:https://wenku.baidu.com/view/6c03a2235acfa1c7aa00ccaf.html
关于 Webdriver API 的学习以及使用方法:

WebElement 对象提供了多种定位元素策略:可通过 ID、Name、ClassName、TagName、LinkText、Xpath 等定位元素。

参考文章:http://blog.sina.com.cn/s/blog_68f262210102vlie.html

1.3 Driver 是什么?

经常说 WebDriver、 RemoteWebDriver、AppiumDriver、AndroidDriver,那什么是 Driver ,它是用来做什么的呢?

各个 Driver 之间的继承(区别):http://discuss.appium.io/t/what-is-the-use-or-difference-between-androiddriver-iosdriver-appiumdriver-and-remote-webdriver/8750

二、APP UI 自动化

2.1 APP UI 自动化的实质

  1. 发送指令,取得元素;
  2. 计算元素坐标;
  3. 根据指令具体内容,自动化驱动程序进行操作。

2.2 各个自动化框架之间的区别

我们知道了 APP UI 自动化的实质,所以现在流行的几个自动化工具做 UI 自动化的实质都一样,都是上面的 3 个步骤。它们的区别是:

  1. 怎么发送指令?谁来把这个指令告诉手机?
  2. 手机用什么驱动自动化点击等操作(即自动化驱动程序是什么)?

2.3 常见 UI 自动化测试框架

下面详细看看 Appium 原理。

三、Appium 原理

自动化驱动框架有

我们再回顾各个自动化测试框架之间的区别

  1. 怎么发送指令?谁来把这个指令告诉手机?
  2. 手机用什么驱动自动化点击等操作(即自动化驱动程序是什么)?

那么 Appium 是如何实现的呢?

3.1 Appium 原理——android 架构图

  1. 怎么发送指令?谁来把指令告诉手机?
    Appium Client 端 脚本执行的时候,Client 端把每一行代码发送到 Appium server 端,Appium server 负责接收脚本发送过来的内容,把脚本发送过来的内容进行翻译,翻译成一个统一的指令

    • 不论你用什么语言编写的脚本都会翻译成统一的命令,所以 appium 支持多语言;
    • 为什么能支持多语言呢,是因为 Appium 继承了 selenium/WebDriver 的 client 的类;
    • 虽然支持不同的语言,但你只要学习不同语言的 API,只需要看不同语言的脚本文档。
  2. 我们知道 Appium 不是一个自动化驱动程序,不去操作手机做自动化,那是谁来操作指令,指令又是怎么传到手机上的呢?
    Appium 提供了一个 bootstrap jar 包,Appium 会在运行的时候偷偷注入到手机上,Appium server 跟自己偷偷注入的这个 jar 包进行通信,jar 包拿到指令后,去调用手机的 UIAUTOMATOR 去做执行

所以 Appium 是一个 server,起调度作用,把指令告诉手机里的 bootstrap.jar;真正执行自动化操作的是 Google 官方提供的自动化工具。

四、UIAUTOMATOR 框架

Android 的自动化框架 UIAutomator ,UIAutomator 框架提供了 3 个非常重要的类:

  1. 对系统硬件按钮、坐标等操作的 Device 类(对 android 系统硬件按钮、坐标等进行点击、滑动如 menu 键、home 键、拖动);
  2. 定位元素控件的 UIObject 类;
  3. 进阶操作的 UIScrollable 类。

五、Client 端脚本编写

5.1 用例的组成

以 Python 为类

  1. 申明测试类,如 Class SimpleAndroidTests(unittest.TestCase),(继承 unittest 的 TestCase 类);
  2. 这个类里主要有 3 种类型的方法:setUp()、tearDown()、test_xxx()(以 test 开头的方法,这就是测试用例,可以有多个); 当有多个测试方法时脚本执行顺序是:setUp -> method1 -> tearDown -> setUp -> method2 -> tearDown。下面详细看看这几个方法做了哪些事情?

5.2 setUp 方法

首先我们看看 setUp 方法 写了什么?

def setUp(self):
    desired_caps = {}
    desired_caps['platformName'] = 'Android'
    desired_caps['platformVersion'] = '6.0.1'
    desired_caps['deviceName'] = 'Android Emulator'
    desired_caps['app'] = PATH(
        '../../../sample-code/apps/ApiDemos/bin/ApiDemos-debug.apk'
    )

    self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

setUp 方法先定义了 desired_caps,然后通过传入 desired_caps 实例化一个 self.driver,实例化过程做的事情有:
1.去 desired_caps 指定的手机机型系统删除原来手机里已有的 desired_caps 指定的 APP;
2.再重新安装 desired_caps 指定的 APP。

所以执行第 2 个测试用例时,登录态等会丢失(如果在第 1 个测试用例时登录了)。如果希望第 2 个测试用例不卸载重新安装,可以加一个参数配置:desired_caps['noRest']=true。但是这样又会有一个问题,下次执行第 1 个测试用例也不会卸载重装 APP 了。

desired_caps 设置的参数决定了 driver 启动时到底以什么状态来启动。那 desired_caps 设置的参数有哪些呢?可以在 Appium 的 GitHub 里查看:
输入 https://github.com/appium/appium,点击 docs 目录,点击 cn 目录,点击 writing-running-appium 目录,看到 caps.md 文件。

这里推荐另一个方法:python 装饰器 @classmethod

在 setUp 和 tearDown 方法前写上装饰器@Classmethod,表示该方法针对这个类只执行一次,那么脚本执行顺序就变成了:setUp -> method1 -> method2 -> tearDown。这样第 1 个测试用例执行前就会卸载重新安装,直到这次所有测试用例执行完。这时可以把登录等操作放在 driver 实例化好且测试用例执行之前,如下代码所示:

self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
       loginAction()

3.self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps), 这行代码什么时候执行完毕呢?
在手机上 APP 启动且能看到页面,这行代码才算执行完毕,即 self.driver 实例成功,才会执行测试用例。

5.3 test_xxx 方法(即测试用例)

  1. 有哪些 API 可以调用,查看 API 文档
    如果不知道 Appium 提供了哪些可以 操作手机的 API ,可以查看 Appium 各个 Client 端 API 文档

    输入 https://github.com/appium/python_client,点击 appium 目录,点击 webdriver 目录,看到 webdriver.py 模块。
    这里是 Python Client 里面实例化的 self.driver 所能调用的所有的 API(也是 webdriver 的源码)。

  2. 知道有哪些 API 了,那怎么定位元素呢?
    可借助 UIAutomatorviewer 工具进行元素定位(或者 Appium 的 inspect)。打开 UIAutomatorviewer 工具的方法是: 在命令行输入 uiautomatorviewer 即可启动。

    常用来定位元素的信息有:id、text、class、content_desc,他们对应的 API 是:by_id、by_text、by_class_name、by_accessibilityId,如果前三者都不能定位到元素,可以请开发帮忙把元素的 content_desc 信息加上,这样可以用 by_accessibilityId 定位元素。

  3. 提高脚本流畅度和稳定的 2 个方法 — 摒弃 time.sleep 线程等待方法
    确定页面有某个控件,但是脚本报错说找不到控件,这时候我们一般会在脚本里加 time.sleep 即线程等待。不建议用这种方法,更好的方法是:

    • 隐式等待 implicitly_wait() 在 driver 生成的时候,设置隐式等待时间 self.driver.implicitly_wait(10),设置了之后,这个会在全局对所有 find_element_by_xxx 生效,如果某个控件没有马上找到,那么在 10s 以内循环的不停的去找这个控件直到找到,如果 10s 后仍然没有找到控件才会报错。
    • Webdriver wait 指定等待,直到某个控件出现才会做 sth。
  4. 如何同时对多台设备执行自动化测试?
    启多个 Appium server 就可以。

    参数配置参考如下文档:输入 https://github.com/appium/appium,点击 docs 目录,点击 cn 目录, server-args 文件。

六、Hybrid 页面自动化测试

对 Hybrid 页面进行自动化操作有两种方式: UIAutomator 以及 UIAutomator + ChromeDriver 。

6.1 通过 UIAutomator 框架

H5 页面是一个 WebView,UIAutomator 不支持操作 WebView 元素,一般情况下只认识 Android View(即 Native 页面)。
UIAutomator 会把 WebView 里所有元素转义成 Android View,但是大部分元素的 id、text 都为空,content_desc 内容 原 text 内容,所以这种情况下不能通过 id、text 信息来定位元素,可以使用 content_desc 信息定位元素。
但是这种方式并不是太通用,因为不同手机机型系统 UIAutomator 将 WebView 里元素转义元素的 content_desc 值不能保持一致的格式,如有的手机将某元素转义后的 content_desc 值为 “登录”,有的手机却是 “u 登录” 等等。
这里 Google 提供了一种更好的方法,即第二种方法。

6.2 通过 UIAutomator + ChromeDriver 框架

在 Android 4.4 系统以后 Google 推出了 ChromeDriver,它跟 UIAutomator 是独立平行的,他是可以操作 WebView 的。
如果是 Hybrid 页面, bootstrap.jar 调起 ChromeDriver 完成自动化;如果是 Native 页面, bootstrap.jar 调起 UIAutomator 完成自动化。
所以 Appium 可以通过 bootstrap.jar 同时调起 UIAutomator 和 ChromeDriver 来完成 Hybrid 的自动化(只能针对 API > 4.3,是受 ChromeDriver 的限制)。

6.3 Hybrid 测试和 Native 测试有什么区别?

基本没什么区别,除了下面这一点。
前面提到:“如果是 Hybrid 页面, bootstrap.jar 调起 ChromeDriver 完成自动化,如果是 Native 页面, bootstrap.jar 调起 UIAutomator 完成自动化。” 那么在脚本里如何实现呢?
通过切换上下文 Context

6.4 Hybrid 涉及的 API

具体 API 可以查看 GitHub:https://github.com/appium/python-client/blob/master/appium/webdriver/webdriver.py

6.5 Chrome 框架如何获取 H5 页面元素

  1. 真机上 APP 的 WebView 的 debug 属性要打开。

    一般 debug 包默认是打开的,如果没有打开找开发协助,模拟器没有这个限制。

  2. Chrome 浏览器输入:chrome://inspect,点击页面上的 inspect 按钮

    需要访问 Google 源,所以需要 ***!用右上角的定位工具(类似 chrome 网页定位元素)定位到元素,在右侧默认选择这块代码,右键选择:复制 - 复制 xpath。

  3. 不能 *** 又想精准定位元素的 xpath

print(self.driver.page_source)

如果你没有可能在 chrome 上打不开上述页面,也可以采用下面的方法:输出 h5 页面的 page_source,保存 html 文件,用 chrome 浏览器打开,右键选择 “ 检查 ”,再按照步骤 2 也可以获得元素的 xpath。最好能用步骤 2。

6.6 易踩的坑

  1. Native 与 WebView 之间的衔接

    一定要记得当前在哪个 Context 下

  2. WebView Context 请不要乱用手势操作

    由于在 WebView Context 下,自动化驱动框架是 ChromeDriver,而 flick、swipe(移动手机特有的操作)是 UIAutomator 框架支持的。 所以只能用 ChromeDriver 针对 web 端支持的手势 API 如 scroll 操作。

  3. 不要忘记 WindowHandle 的存在

    列表页有 2 个商户,假设商户详情页都是 WebView 页面,进入第 1 个商户详情页是一个 WebView,进入第 2 个商户详情页可能不是一个同一个 WebView,可能新起了一个 WebView。这个其实很正常,和 Chrome 浏览器类似,可以是在当前 tab 打开第 2 个商户页,也可能是新起一个 tab 打开商户页。
    如果明明切换了 Context 但是还是不能定位当前页面元素,有可能 Context 切换了,但是 WindowHandle 还留在上一个 WebView 页面,需要对 WindowHandle 做一个切换。

七、其他——Linux 命令

  1. open . ,通过终端打开 Finder
  2. tree -L 2 ,以树状图列出文件目录 2 层
  3. subl 文件名 , 表示编辑文件(没有则新建文件)
  4. subl . ,使用 sublime text 打开当前文件夹
  5. aapt dump badging 包名 ,得到包的相关信息


↙↙↙阅读原文可查看相关链接,并与作者交流