zoukankan      html  css  js  c++  java
  • app自动化03

    移动端自动化测试工具

    UIAutomatorViewer

    主流的移动端自动化工具

    - Robotium
        1.支持语言:Java
        2.仅支持Android系统
        3.不支持跨应用
    - Macaca
        1.支持语言:Java,Python,Node.js
        2.支持Android和iOS系统
        3.支持跨应用
    - Appium
         1.支持语言:Java,C#,Python,php,perl,ruby,Node.js
         2.支持Android和iOS系统
         3.支持跨应用
    
    自动化工具选择的关注点
     1.是否支持native(android原生用java写的页面),webview(用html写的页面)
        (原生应用(原生的java代码写的,没用到html页面,如电话本软件),android应用层就是java,软件里面的控件:按钮等,都是通过java写的)
         webview(用纯的html写的页面,如手机浏览器打开知乎等,都是全部用html写的,没用到java)
        混合应用(原生的java代码写的,用到html页面,如淘宝客户端{商品广告页就是用html,淘宝底部控件就是用java写的})
    2.是否支持获取toast(弹出的窗口,如手机无网络时会弹出网络不可用的窗口)
    3.是否支持跨应用(美团外卖-->支付宝支付-->美团外卖)

    Appium介绍

    Appium是一个移动端的自动化框架,可用于测试原生应用,移动网页应用和混合型应用,且是跨平台的。
    可用于iOS和Android以及firefox的操作系统。
    原生的应用是指用android或ios的sdk编写的应用,移动网页应用是指网页应用,类似于ios中safari应用或者Chrome应用或者类浏览器的应用。
    混合应用是指一种包裹webview的应用,原生应用与网页内容交互性的应用。
    重要的是Appium是跨平台的,何为跨平台,意思就是可以针对不同的平台用一套api来编写测试用例。

    Appium特点

    1.使用自动化来测试一个app,但是不需要重新编译它
    2.写自动化case,不需要学习特定的语言
    3.一个自动化框架不需要重复造轮子
    4.一个自动化框架需要开源,在精神和实践上实现开源

    Appium自动化测试环境搭建

    我们使用Appium和python来进行自动化测试,需要安装两个东西,一个是Appium的客户端,一个是Appium-python库。
    这两个需要安装的东西在加上手机就可以进行自动化测试,它们之间的关系是:
         python代码 (操控)-> Appium-python库 ->  (操控)Appium -> (操控) 手机。

    Appium客户端安装

    1.官网:www.appium.io,由SauceLab公司开发
    
    2.Appium是由nodejs的express框架写的Http Server,Appium使用WebDriver的json wire协议,
        来驱动Apple系统的UIAutomation库、Android系统的UIAutomator框架
    Appium桌面客户端安装方式
    1. 运行appium-desktop-Setup-1.2.7.exe,默认安装即可
    2. 启动客户端,按图片步骤 1 -> 2 -> 3 -> 4 设置
    3. 启动成功展示如下图

    Appiu命令行安装(**牢记)

    1. 安装Node.js ->Win:官网下载可执行包安装(Linux: yum install; Macos: brew install)
    2. 安装完成后 命令行运行npm或node -v 来查看是否安装成功
    敲黑板: npm国内一般被墙,所以选择淘宝镜像安装,官网:http://npm.taobao.org
    3. 安装cnpm: npm install -g cnpm --registry=https://registry.npm.taobao.org
    4. 安装appium: cnpm install -g appium
    5. 启动appium服务命令: appium &,如下图即正确安装
    敲黑板: Windows安装会提示os的模块错误,这个需要mac系统支持,不影响windows操作使用

    Appium-python库安装

    命令行安装(需要联网)
    pip3 install Appium-Python-Client(pip3是当你电脑装了python2和python3,告诉电脑安装python3里面,如果电脑只有一个python,可以直接pip)

    Hello Appium

    需求

    使用Python打开android模拟器中的设置界面。

    思路

    python代码到手机的过程是需要先经过Appium-python库再经过Appium再到手机。
    也就是python代码 -> Appium-python库 -> Appium -> 手机。(Appium底部其实也是通过adb命令形式,操纵手机的)

    方法

    from appium import webdriver
    import time
    # server 启动参数,用字典存储参数信息
    desired_caps = {}
    # 设备信息
    desired_caps['platformName'] = 'Android'  # 平台的名称
    desired_caps['platformVersion'] = '5.1'   # 系统版本号
    desired_caps['deviceName'] = '192.168.56.101:5555'   # 安卓随便写 但是不能是空字符串
    # app信息
    desired_caps['appPackage'] = 'com.android.settings'  # 需要打开的程序包名
    desired_caps['appActivity'] = '.Settings'             # 需要打开的页面,启动名
    
    driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)  # Appium-python库连接Appium
    
    # time.sleep(2)
    
    # driver.quit()

    Appium基础API

    前置代码

    from appium import webdriver
    import time
    # server 启动参数,用字典存储参数信息
    desired_caps = {}
    # 设备信息
    desired_caps['platformName'] = 'Android'  # 平台的名称
    desired_caps['platformVersion'] = '5.1'   # 系统版本号
    desired_caps['deviceName'] = '192.168.56.101:5555'   # 安卓随便写 但是不能是空字符串
    # app信息
    desired_caps['appPackage'] = 'com.android.settings'  # 需要打开的程序包名
    desired_caps['appActivity'] = '.Settings'             # 需要打开的页面,启动名
    # 解决中文无法输入的问题
    desired_caps['unicodeKeyboard'] = True
    desired_caps['resetKeyboard'] = True
    # 声明driver对象
    driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)

    获取app包名和启动名

        # 通过driver对象调用
        获取包名方法:current_package
        获取启动名:current_activity
        业务场景:
            1.启动设置
            2.获取包名和启动名
        代码实现:
            print(driver.current_package)
            print(driver.current_activity)
        执行结果:
            com.tencent.news
            .activity.SplashActivity

    脚本内启动其他app

    driver.start_activity(appPackage,appActivity)
        参数:
            appPackage:包名
            appActivity:启动名
        示例:
            driver.start_activity('com.android.mms', '.ui.ConversationList')

    关闭app

    driver.close_app()  # 关闭当前操作的app,不会关闭驱动对象,driver还能用

    关闭驱动对象

    driver.quit()   # 关闭驱动对象,同时关闭所有关联的app,连driver一同关掉,driver就相当于一个大管家,管理手机,不能再用
                    # driver帮你做事情,否则会报错

    安装APK到手机

        driver.install_app(app_path) 
        参数:
            app_path:脚本机器中APK文件路径
        示例:
            driver.install_app("/Users/Yoson/Downloads/anzhishichang_6450.apk")

    手机中移除APP

    driver.remove_app(app_id) 
        参数:
            app_id:需要卸载的app包名
        示例:
            driver.remove_app('cn.goapk.market')

    判断APP是否已安装

    driver.is_app_installed(app_id) 
        参数:
            bundle_id: 可以传入app包名,返回结果为True(已安装) / False(未安装)
        示例:
        print(driver.is_app_installed('cn.goapk.market'))

    发送文件到手机

        import base64
        data = str(base64.b64encode(data.encode('utf-8')),'utf-8')
        driver.push_file(path,data)
        参数:
            path:手机设备上的路径(例如:/sdcard/a.txt)
            data:文件内数据,要求base64编码
            Python3.x中字符都为unicode编码,而b64encode函数的参数为byte类型,需要先转码;生成的数据为byte类型,需要将byte转换回去。
        示例:
            import base64
            data = str(base64.b64encode('test 123'.encode('utf-8')), 'utf-8')
            driver.push_file('/sdcard/test.txt', data)

    从手机中拉取文件

        import base64
        data = driver.pull_file(path) # 返回数据为base64编码
        print(str(base64.b64decode(data),'utf-8')) # base64解码
        参数:
            path: 手机设备上的路径
        示例:
            import base64
            data = driver.pull_file('/sdcard/test.txt') # 返回数据为base64编码
            print(str(base64.b64decode(data), 'utf-8')) # base64解码

    获取当前屏幕内元素结构

    driver.page_source  
        作用:
            返回当前页面的文档结构,判断特定的元素是否存在
        示例:
            print(driver.page_source)  (如果某些div或元素是隐藏的,可以用这个方法获取所有隐藏的元素)

    应用置于后台事件

    APP放置后台,模拟热启动
        方法:background_app(seconds)
        参数:
            1.seconds:停留在后台的时间,单位:秒
    业务场景:
            1.进入设置页
            2.将APP置于后台5s
    代码实现:
            driver.background_app(5)   (很常用)
    效果:
            app置于后台5s后,再次展示当前页面,即回到被置于后台的页面

    工具简介

    用来扫描和分析Android应用程序的UI控件的工具,类似于电脑端浏览器的Firebug控件的功能.

    如何使用

    1.进入AndroidSDK目录下的tools目录,打开uiautomatorviewer
    2.电脑连接真机或打开android模拟器
    3.启动待测试app
    4.点击uiautomatorviewer的左上角Device Screenshot,会生成app当前页面的UI控件截图
    注意:只出现当前手机页面所显示元素,如手机里面控件太多,那么没显示的不会截图

    元素定位API

    手工测试主要通过可见按钮操作,而自动化是通过元素进行交互操作.
    ⚠️⚠️⚠️ 元素的基本定位基于当前屏幕范围内展示的可见元素。
    * Appium常用元素定位方式
    前置代码
    from appium import webdriver
    # server 启动参数
    desired_caps = {}
    # 设备信息
    desired_caps['platformName'] = 'Android'
    desired_caps['platformVersion'] = '5.1'
    desired_caps['deviceName'] = '192.168.56.101:5555'
    # app的信息
    desired_caps['appPackage'] = 'com.android.settings'
    desired_caps['appActivity'] = '.Settings'
    
    # 声明我们的driver对象
    driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)

    定位一个元素 element

    通过id定位

    方法:find_element_by_id(id_value) # id_value:为元素的id属性值
    业务场景:
    		1.进入设置页面
    		2.通过ID定位方式点击搜索按钮

    通过class定位

    	方法:find_element_by_class_name(class_value) # class_value:为元素的class属性值
    业务场景:
    		1.进入设置页面
    		2.点击搜索按钮
    		3.通过class定位方式点击输入框的返回按钮
    	代码实现:
    		# id 点击搜索按钮
    		driver.find_element_by_id("com.android.settings:id/search").click()
    		# class 点击输入框返回按钮
    		driver.find_element_by_class_name('android.widget.ImageButton').click()
    		driver.quit()

    通过xpath定位

    	方法:find_element_by_xpath(xpath_value) # xpath_value:为可以定位到元素的xpath语句
    *** android端xptah常用属性定位:
    		1. id ://*[contains(@resource-id,'com.android.settings:id/search')] 
    		2. class ://*[contains(@class,'android.widget.ImageButton')]
    		3. text ://*[contains(@text,'WLA')]
    
    *** 模糊定位 contains(@key,value): value可以是部分值
    业务场景:
    		1.进入设置页面
    		2.点击WLAN菜单栏
    代码实现:
    		# xpath 点击WLAN按钮
    	    driver.find_element_by_xpath("//*[contains(@text,'WLA')]").click()

    定位一组元素 elements

    应用场景为元素值重复,无法通过元素属性直接定位到某个元素,只能通过elements方式来选择,返回一个定位对象的列表.

    通过id方式定位一组元素

    方法: find_elements_by_id(id_value) # id_value:为元素的id属性值
    业务场景:
    		1.进入设置页面
    		2.点击WLAN菜单栏(id定位对象列表中第1个)
    代码实现:
    	# 定位到一组元素
    	title = driver.find_elements_by_id("com.android.settings:id/title")
    	# 打印title类型,预期为list
        print(type(title))
        # 取title返回列表中的第一个定位对象,执行点击操作
        title[0].click()

    通过class方式定位一组元素

    方法:find_elements_by_class_name(class_value) # class_value:为元素的class属性值
    业务场景:
    	1.进入设置页面
    	2.点击WLAN菜单栏(class定位对象列表中第3个)
    	代码实现:
    		# 定位到一组元素
    		title = driver.find_elements_by_class_name("android.widget.TextView")
    		# 打印title类型,预期为list
        	print(type(title))
        	# 取title返回列表中的第一个定位对象,执行点击操作
        	title[3].click()

    通过xpath定位一组元素

    方法:find_elements_by_xpath(xpath_value) # xpath_value:为可以定位到元素的xpath语句
    业务场景:
    	1.进入设置页面
    	2.点击WLAN菜单栏(xpath中class属性定位对象列表中第3个)
    代码实现:
    	# 定位到一组元素
    	title = driver.find_elements_by_xpath("//*[contains(@class,'widget.TextView')]")
    	# 打印title类型,预期为list
        print(type(title))
        # 取title返回列表中的第一个定位对象,执行点击操作
        title[3].click()

    WebDriverWait显示等待

    在一个超时时间范围内,每隔一段时间去搜索一次元素是否存在,
    如果存在返回定位对象,如果不存在直到超时时间到达,报超时异常错误。
    // 在Appium中用了Selenium中造的轮子(显示等待)
    方法:WebDriverWait(driver, timeout, poll_frequency).until(method)
    	参数:
    		1.driver:手机驱动对象
    		2.timeout:搜索超时时间
    		3.poll_frequency:每次搜索间隔时间,默认时间为0.5s
    		4.method:定位方法(匿名函数)
    	匿名函数: 
    		lambda x: x
    	等价于python函数:
    		def test(x):
        		return x
    使用示例:
    		from selenium.webdriver.support.wait import WebDriverWait
    		WebDriverWait(driver, timeout, poll_frequency).until(lambda x: x.find_elements_by_id(id_value))
    	解释:
    		1.x传入值为:driver,所以才可以使用定位方法.
    	函数运行过程:
    		1.实例化WebDriverWait类,传入driver对象,之后driver对象被赋值给WebDriverWait的一个类变量:self._driver
    		2.until为WebDriverWait类的方法,until传入method方法(即匿名函数),之后method方法会被传入self._driver
    		3.搜索到元素后until返回定位对象,没有搜索到函数until返回超时异常错误.
    业务场景:
    		1.进入设置页面
    		2.通过ID定位方式点击搜索按钮
    	代码实现:
    		from selenium.webdriver.support.wait import WebDriverWait # 导入WebDriverWait类
    		# 超时时间为30s,每隔1秒搜索一次元素是否存在,如果元素存在返回定位对象并退出
    		search_button = WebDriverWait(driver, 30, 1).until(lambda x: x.find_elements_by_id(com.android.settings:id/search))
    		search_button.click()
    		driver.quit()

    元素操作API

    	本节讲介绍手机端元素信息的获取以及基本的输入操作。
    前置代码
    	from appium import webdriver
    	# server 启动参数
    	desired_caps = {}
    	# 设备信息
    	desired_caps['platformName'] = 'Android'
    	desired_caps['platformVersion'] = '5.1'
    	desired_caps['deviceName'] = '192.168.56.101:5555'
    	# app的信息
    	desired_caps['appPackage'] = 'com.android.settings'
    	desired_caps['appActivity'] = '.Settings'
    
    	# 声明我们的driver对象
    	driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)

    点击元素

    方法:click() 
    业务场景:
    		1.打开设置
    		2.点击搜索按钮
    代码实现:
    		# 点击搜索按钮
        	driver.find_element_by_id("com.android.settings:id/search").click()

    发送数据到输入框

    方法:send_keys(vaue) # value:需要发送到输入框内的文本
    	业务场景:
    		1.打开设置
    		2.点击搜索按钮
    		3.输入内容abc
    	代码实现:
    		# 点击搜索按钮
        	driver.find_element_by_id("com.android.settings:id/search").click()
        	# 定位到输入框并输入abc
        	driver.find_element_by_id("android:id/search_src_text").send_keys("abc")
    
        重点:
        	大家可以将输入的abc 改成 输入中文,得到的结果:输入框无任何值输入且程序不会抱错
    	解决输入中文问题:
    
    		1.server 启动参数增加两个参数配置
    			desired_caps['unicodeKeyboard'] = True
    			desired_caps['resetKeyboard'] = True
    
    		2.再次运行会发现运行成功
    			# 点击搜索按钮
    	    	driver.find_element_by_id("com.android.settings:id/search").click()
    	    	# 定位到输入框并输入abc
    	    	driver.find_element_by_id("android:id/search_src_text").send_keys("传智播客")

    清空输入框内容

    方法:clear()
    	业务场景:
    		1.打开设置
    		2.点击搜索按钮
    		3.输入内容abc
    		4.删除已输入abc
    代码实现:
    		# 点击搜索按钮
    	    driver.find_element_by_id("com.android.settings:id/search").click()
    	    # 定位到输入框并输入abc
    	    input_text = driver.find_element_by_id("android:id/search_src_text")
    	    # 输入abc
    	    input_text.send_keys("abc")
    	    time.sleep(1)
    	    # 删除abc
    	    input_text.clear()

    获取元素的文本内容

    方法: text
    业务场景:
    		1.进入设置
    		2.获取所有元素class属性为“android.widget.TextView”的文本内容
    代码实现:
    		text_vlaue = driver.find_elements_by_class_name("android.widget.TextView")
    	    for i in text_vlaue:
    	        print(i.text)
    执行结果:
    		设置
    
    		无线和网络
    		WLAN
    		更多
    		设备
    		显示
    		提示音和通知
    		存储

    获取元素的属性值

    方法: get_attribute(value) # value:元素的属性
    	⚠️ value='name' 返回content-desc / text属性值(若一者为空,返回另一者值)
    	⚠️ value='text' 返回text的属性值
    	⚠️ value='className' 返回 class属性值,只有 API=>18 才能支持
    	⚠️ value='resourceId' 返回 resource-id属性值,只有 API=>18 才能支持
    	业务场景:
    		1.进入设置
    		2.获取搜索按钮的content-desc属性值
    代码实现:
    		# 定位到搜索按钮
    		get_value = driver.find_element_by_id("com.android.settings:id/search")
        	print(get_value.get_attribute("name"))
    执行结果:
        	搜索

    获取元素在屏幕上的坐标

    方法:location
    业务场景:
    		1.进入设置页面
    		2.获取搜索按钮在屏幕的坐标位置
    代码实现:
    		# 定位到搜索按钮
    	    get_value = driver.find_element_by_id("com.android.settings:id/search")
    	    # 打印搜索按钮在屏幕上的坐标
    	    print(get_value.location)
    执行结果:
    		{'y': 44, 'x': 408} 元素左上角到屏幕左上角的距离