Python+appium app自动化

ce

一 Appium简介

1. 什么是APpium

  • Appium是一个移动端的自动化测试框架,可用于测试原生应用移动网页应用和混合型应用,且是跨平台的
    • 原生的应用指androi或ios的sdk编写的应用
    • 移动网页应用是指网页应用,类似于ios中的Safari或Chrome应用
    • 混合型应用是介于web-app和native-app之间的应用
  • Appium是模拟人的操作进行功能自动化,通常用于功能测试和兼容性测试

2. Appium的设计

  • 采用底层驱动商提供的自动化框架
    • IOS : 苹果的UIAutonmation
    • Android4.2+ : 谷歌的UIAutomator
  • 采用底层驱动商提供统一API,即WebDriverAPI
    • WebDriver(也称Selenium WebDriver)是一个C/S架构的协议,通过这个协议,用任何语言写成的客户端都可以发送HTTP请求给服务器,也就是说可以自由选择想要的测试框架和执行器,也可以将任何包含HTTP客户端的库文件加入到你的代码中。
  • 在WEbDriver的基础上,扩展了一些适合移动端自动化协议的API

3. Appium原理

  • 核心是Web服务器
    • 它接受客户端的连接,执行客户端的命令,在手机设备上执行命令,然后通过HTTP的响应手机命令执行的结果
  • Session
    • 自动化的过程通常在Session上下文执行
    • 客户端初始化一个Session会话,虽然不同的语言初始化的方式不同,但他们都要发送POST/session请求到服务器,这个请求里面会带有一个对象:desired capabilities,这个时候服务器端会启动自动化session然后返回一个session ID,以后的命令都会用这个session ID去匹配
  • Desired Capabilities
    • Desired Capabilities对象其实一个key-value的集合,里面包含了各种各样的信息,发送到服务器端后,服务器解析这些嘻嘻就知道 客户端端对哪种session感兴趣,然后会启动相应的session
    • 里面的信息会影响服务器启动session的类型,比如你platformName值是ios,就会告诉服务器启动一个ios的session,而不是Android sess,之后适应js打开新窗口
  • Appium Server
    • Appium 是一个用Node。js编写的HTTP Server,它创建并管理多个WebDriver session来和不同平台交互,如IOS,Android等
    • Appium Clients
      • Appium开始一个测试后,就会在被测设备(手机)上启动一个Server监听来自Appium Server的指令,每种平台像IOS和Android都有不同的运行和交互方式,所以Appium会用米格桩程序“侵入”该平台,并接受指令,来完成测试用例的运行
      • 客户端的概念不是我们传统意义上的客户端,这里的客户端更好的理解是一个扩展WebDriver的协议库,你在用自己喜欢的语言写case的时候,将该语言扩展WebDriver的库添加到你的环境,你就可以理解这是个客户端

二 Appium环境搭建

1. 基础环境

  • 安装Python

  • 安装jdk并配置环境变量

    • JAVA_HOME d:\tools\java\jdk1.8.0_211
  • 安装 配置Android SDK : https://www.androiddevtools.cn/

Python+appium app自动化_第1张图片

  • 解压完成后,需要 配置一下 添加一个 环境变量 ANDROID_HOME ,设置值为sdk包解压目录,比如 d:\tools\androidsdk

  • 配置环境变量 PATH ,加入 adb所在目录, d:\tools\androidsdk\platform-tools\

  • 安装Appium Server : https://github.com/appium/appium-desktop/releases/tag/v1.15.1

  • 安装Appium Python Client

    • Appium Client 是配合原生的WebDriver来使用的

      pip install Appium-Python-Client 
      利用命令pip list查看安装列表查找是否已经安装上
      
  • 连接手机

    • 手机开启开发者模式
    • 启动usb调试

2. 通过Python编写自动化测试脚本

  • 导包 : from appium import webdriver

  • 设置desired_capabilities

    • 创建字典 : desired_caps = {}

    • 指定设备信息

      desired_caps = {
        'platformName': 'Android', # 被测手机是安卓
        'platformVersion': '8', # 手机安卓版本
        'deviceName': 'xxx', # 设备名,安卓手机可以随意填写
        'appPackage': 'tv.danmaku.bili', # 启动APP Package名称
        'appActivity': '.ui.splash.SplashActivity', # 启动Activity名称
        'unicodeKeyboard': True, # 使用自带输入法,输入中文时填True
        'resetKeyboard': True, # 执行完程序恢复原来输入法
        'noReset': True,       # 不要重置App
        'newCommandTimeout': 6000,
        'automationName' : 'UiAutomator2'
      }
      
      ---------------------------------------------------------
      
      # 指定移动平台操作系统
      desired_caps["platformName"] = "Android"
      # 指定操作系统版本
      desired_caps["platformVersion"] = "8.0.0"
      # 指定设备 可通过adb device查看
      desired_caps["deviceName"] = "62963179"
      # 指定需要测试的app的程序包名
      desired_caps["appPackage"] = "com.miui.calculator"
      # 指定启动页面的名字
      desired_caps["appActivity"] ="cal.CalculatorActivity"
      '''
      查看app启动页Activity
      1. 使用adb shell 命令进入Android端shell命令界面
      2. 执行命令 : dumpsys package 包名
          在前几行找到带有Activity的一串即为启动页面
      '''
      
    • 首先打开appium服务

    • 启动app

      driver=webdriver.Remote("http://localhost:4723/wd/hub",desired_caps)
      
  • appium保存能力

    {
    "platformName":"Android",
    "platformVersion":"8.0.0",
    "deviceName":"62963179",
    "appPackage":"com.miui.calculator",
    "appActivity":"cal.CalculatorActivity"
    }
    

Python+appium app自动化_第2张图片

Python+appium app自动化_第3张图片

Python+appium app自动化_第4张图片
这样软件就会识别并截图app应用,为接下来的定位元素做准备

  • 操作元素

    driver.find_element_by_id("xxx").click() #定位元素并点击
    
  • 指定每次运行测试前不重新安装被测试app

    desired_caps["noReset] = True

  • 简单测试miui的计算器

    # 导包
    from appium import webdriver
    import time
    
    # 设置desired_capablities 字典
    desired_caps = {}
    # 指定移动平台操作系统
    desired_caps["platformName"] = "Android"
    # 指定操作系统版本
    desired_caps["platformVersion"] = "8.0.0"
    # 指定设备 可通过adb device查看
    desired_caps["deviceName"] = "62963179"
    # 指定需要测试的app的程序包名
    desired_caps["appPackage"] = "com.miui.calculator"
    # 指定启动页面的名字
    desired_caps["appActivity"] ="cal.CalculatorActivity"
    # 指定每次运存测试前重新安装测试的app
    desired_caps['noReset'] = True
    '''
    查看app启动页Activity
    1. 使用adb shell 命令进入Android端shell命令界面
    2. 执行命令 : dumpsys package 包名
        在前几行找到带有Activity的一串即为启动页面
    '''
    
    # 启动app
    driver = webdriver.Remote("http://localhost:4723/wd/hub",desired_caps)
    
    # 设置缺省等待时间
    driver.implicitly_wait(5)
    
    # 操作元素 (自动完成2+5的操作)
    driver.find_element_by_id("com.miui.calculator:id/btn_2_s").click()  # 点击事件
    driver.find_element_by_id("com.miui.calculator:id/btn_plus_s").click()  # 点击事件
    driver.find_element_by_id("com.miui.calculator:id/btn_5_s").click()  # 点击事件
    
    # driver.find_element_by_xpath('//android.widget.ImageView[@content-desc="等于"]').click()  # 点击事件
    # 回车代替等号
    driver.press_keycode(66)
    time.sleep(5)
    # 退出app
    driver.quit()
    
  • 打开B站

    from appium import webdriver
    from appium.webdriver.extensions.android.nativekey import AndroidKey
    
    desired_caps = {
      'platformName': 'Android', # 被测手机是安卓
      'platformVersion': '8', # 手机安卓版本
      'deviceName': 'xxx', # 设备名,安卓手机可以随意填写
      'appPackage': 'tv.danmaku.bili', # 启动APP Package名称
      'appActivity': '.ui.splash.SplashActivity', # 启动Activity名称
      'unicodeKeyboard': True, # 使用自带输入法,输入中文时填True
      'resetKeyboard': True, # 执行完程序恢复原来输入法
      'noReset': True,       # 不要重置App
      'newCommandTimeout': 6000,
      'automationName' : 'UiAutomator2'
      # 'app': r'd:\apk\bili.apk',
    }
    
    # 连接Appium Server,初始化自动化环境
    driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
    
    # 设置缺省等待时间
    driver.implicitly_wait(5)
    
    # 如果有`青少年保护`界面,点击`我知道了`
    iknow = driver.find_elements_by_id("text3")
    if iknow:
        iknow.click()
    
    # 根据id定位搜索位置框,点击
    driver.find_element_by_id("expand_search").click()
    
    # 根据id定位搜索输入框,点击
    sbox = driver.find_element_by_id('search_src_text')
    sbox.send_keys('白月黑羽')
    # 输入回车键,确定搜索
    driver.press_keycode(AndroidKey.ENTER)
    
    # 选择(定位)所有视频标题
    eles = driver.find_elements_by_id("title")
    
    for ele in eles:
        # 打印标题
        print(ele.text)
    
    input('**** Press to quit..')
    driver.quit()
    
  • 键盘输入数据 - send_keys(),clear()-清空

    driver.page_source # 获取网页源数据
    
    from appium import webdriver
    import time
    desired_caps = {}
    desired_caps["platformName"] = "Android"
    desired_caps["platformVersion"] = "8.0.0"
    desired_caps["deviceName"] = "62963179"
    desired_caps["appPackage"] = "com.android.browser"
    desired_caps["appActivity"] ="BrowserActivity"
    # 指定每次运存测试前重新安装测试的app
    desired_caps['noReset'] = True
    # 启动app
    driver = webdriver.Remote("http://localhost:4723/wd/hub",desired_caps)
    
    # 操作元素 (用浏览器搜索"appium"关键字)
    driver.find_element_by_id("com.android.browser:id/search_hint").click()
    driver.find_element_by_id("com.android.browser:id/url").send_keys("appium")
    
    # 回车代替等号
    driver.press_keycode(66)
    time.sleep(5)
    # 退出app
    driver.quit()
    

    若不能输入中文,用一下设置

    desired_caps['UnicodeKeyboard']=True
    desired_caps['resetKeyboard']=True
    driver.find_element_by_id("com.android.browser:id/url").send_keys(u"appium")
    

3. 查找 应用 Package 和 Activity

如果你应用已经安装在手机上了,可以直接打开手机上该应用,进入到你要操作的界面

然后执行

adb shell dumpsys activity recents | find "intent={"

会显示如下,最近的 几个 activity 信息,

intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=tv.danmaku.bili/.ui.splash.SplashActivity}
intent={act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000300cmp=com.huawei.android.launcher/.unihome.UniHomeLauncher}
intent={flg=0x10804000 cmp=com.android.systemui/.recents.RecentsActivity bnds=[48,1378][10322746]}
intent={flg=0x10000000 cmp=com.tencent.mm/.ui.LauncherUI}

其中第一行就是当前的应用,我们特别关注最后

cmp=tv.danmaku.bili/.ui.splash.SplashActivity

应用的package名称就是 tv.danmaku.bili

应用的启动Activity就是 .ui.splash.SplashActivity

如果你已经获取到了 apk,在命令行窗口执行

d:\tools\androidsdk\build-tools\29.0.3\aapt.exe dump badging d:\tools\apk\bili.apk | find "package: name="

输出信息中,就有应用的package名称

package: name='tv.danmaku.bili' versionCode='5531000' versionName='5.53.1' platformBuildVersionName='5.53.1' compileSdkVersion='28' compileSdkVersionCodename='9'

在命令行窗口执行

d:\tools\androidsdk\build-tools\29.0.3\aapt.exe dump badging d:\tools\apk\bili.apk | find "launchable-activity"

输出信息中,就有应用的启动Activity

launchable-activity: name='tv.danmaku.bili.ui.splash.SplashActivity'  label='' icon=''

5. 定位元素

Appium是基于Selenium的,所以 和 Selenium 代码 定位元素的 基本规则相同:

  • find_element_by_XXX 方法,返回符合条件的第一个元素,找不到抛出异常
  • find_elements_by_XXX 方法,返回符合条件的所有元素的列表,找不到返回空列表
  • 通过 WebDriver 对象调用这样的方法,查找范围是整个界面
  • 通过 WebElement 对象调用这样的方法,查找范围是该节点的子节点

界面元素查看工具

做 Selenium Web 自动化的时候,要找到元素,我们是通过浏览器的开发者工具栏来查看元素的特性,根据这些特性(属性和位置),来定位元素

Appium 要自动化手机应用,同样需要工具查看界面元素的特征。

常用的查看工具是: Android Sdk包中的 uiautomateviewer 和 Appium Desktop 中的 Appium Inspector

定位元素的方法

根据ID

ID ,是安卓应用元素的 resource-id 属性

使用如下代码

driver.find_element_by_id('expand_search')

根据CLASS NAME

安卓界面元素的 class属性 其实就是根据元素的类型,类似web里面的tagname, 所以通常不是唯一的。

通常,我们根据class 属性来选择元素, 是要选择多个而不是一个。

当然如果你确定 要查找的 界面元素的类型 在当前界面中只有一个,就可以根据class 来唯一选择。

使用如下代码

driver.find_elements_by_class_name('android.widget.TextView')

根据ACCESSIBILITY ID

元素的 content-desc 属性是用来描述该元素的作用的。

如果要查询的界面元素有 content-desc属性,我们可以通过它来定位选择元素。

使用如下代码

driver.find_element_by_accessibility_id('找人')

Xpath

Appium 也支持通过 Xpath选择元素。

但是其可靠性和性能不如 Selenium Web自动化。因为Web自动化对Xpath的支持是由浏览器实现的,而Appium Xpath的支持是 Appium Server实现的。

毕竟,浏览器产品的成熟度比Appium要高很多。

当然,Xpath是标准语法,所以这里表达式的语法规则和 以前学习的Selenium里面Xpath的语法是一样的,比如

driver.find_element_by_xpath('//ele1/ele2[@attr="value"]')

selenium自动化中, xpath表达式中每个节点名是html的tagname。

但是在appium中, xpath表达式中 每个节点名 是元素的class属性值。

比如:要选择所有的文本节点,就使用如下代码

driver.find_element_by_xpath('//android.widget.TextView')

安卓 UIAutomator

根据id,classname, accessibilityid,xpath,这些方法选择元素,其实底层都是利用了安卓 uiautomator框架的API功能实现的。

参考 这里的谷歌安卓官方文档介绍: https://developer.android.google.cn/training/testing/ui-automator

也就是说,程序的这些定位请求,被Appium server转发给手机自动化代理程序,就转化为为uiautomator里面相应的定位函数调用。

其实,我们的自动化程序,可以直接告诉 手机上的自动化代理程序,让它 调用UI Automator API的java代码,实现最为直接的自动化控制。

主要是通过 UiSelector 这个类里面的方法实现元素定位的,比如

code = 'new UiSelector().text("热门").className("android.widget.TextView")'
ele = driver.find_element_by_android_uiautomator(code)
ele.click()

就是通过 text 属性 和 className的属性 两个条件 来定位元素。

UiSelector里面有些元素选择的方法 可以解决 前面解决不了的问题。

比如

  • text 方法

    可以根据元素的文本属性查找元素

  • textContains

    根据文本包含什么字符串

  • textStartsWith

    根据文本以什么字符串开头

  • textmartch 方法

    可以使用正则表达式 选择一些元素,如下

    code = 'new UiSelector().textMatches("^我的.*")'
    

UiSelector 的 instanceindex 也可以用来定位元素,都是从0开始计数, 他们的区别:

  • instance是匹配的结果所有元素里面 的第几个元素
  • index则是其父元素的几个节点,类似xpath 里面的*[n]

UiSelector 的 childSelector 可以选择后代元素,比如

code = 'new UiSelector().resourceId("tv.danmaku.bili:id/recycler_view").childSelector(new UiSelector().className("android.widget.TextView"))'

ele = driver.find_element_by_android_uiautomator(code)

注意: childSelector后面的引号要框住整个 子 uiSelector 的表达式

目前有个bug:只能找到符合条件的第一个元素,参考appium 在github上的 issues:

https://github.com/appium/java-client/issues/150

4. 断言

# 导包
from appium import webdriver
import time

# 设置desired_capablities 字典
desired_caps = {}
# 指定移动平台操作系统
desired_caps["platformName"] = "Android"
# 指定操作系统版本
desired_caps["platformVersion"] = "8.0.0"
# 指定设备 可通过adb device查看
desired_caps["deviceName"] = "62963179"
# 指定需要测试的app的程序包名
desired_caps["appPackage"] = "com.miui.calculator"
# 指定启动页面的名字
desired_caps["appActivity"] ="cal.CalculatorActivity"
# 指定每次运存测试前重新安装测试的app
desired_caps['noReset'] = True
'''
查看app启动页Activity
1. 使用adb shell 命令进入Android端shell命令界面
2. 执行命令 : dumpsys package 包名
    在前几行找到带有Activity的一串即为启动页面
'''

# 启动app
driver = webdriver.Remote("http://localhost:4723/wd/hub",desired_caps)

# 操作元素 (自动完成2+5的操作)
driver.find_element_by_id("com.miui.calculator:id/btn_2_s").click()  # 点击事件
driver.find_element_by_id("com.miui.calculator:id/btn_plus_s").click()  # 点击事件
driver.find_element_by_id("com.miui.calculator:id/btn_5_s").click()  # 点击事件

# driver.find_element_by_xpath('//android.widget.ImageView[@content-desc="等于"]').click()  # 点击事件
# 回车代替等号
driver.press_keycode(66)

# 断言
result = driver.find_element_by_id("com.miui.calculator:id/result").text
print(result)
if result=="= 7":
    print("通过")
else:
    print("失败")

time.sleep(5)
# 退出app
# driver.quit()

5. 获取列表文件

6. 获取文本文件

7. 获取excel文件

  • 安装pandas

    • pip install pandas
  • 获得数据

    import pandas
    
    data = pandas.read_excel("test.xlsx", sheet_name=0, names=["数值1", "运算符", "数值2", "预期结果"],
                             dtype={"数值1": str, "运算符": str, "数值2": str, "预期结果": str},header=None)
    datalist = data.values.tolist() # 将数据转换为二维列表
    

三 界面操作和adb命令

界面操作

click点击

省略

tap点按

WebElement 对象的 tap 方法和 click 类似,都是点击界面。但是最大的区别是, tap是 针对坐标 而不是针对找到的元素。为了保证自动化代码在所有分辨率的手机上都能正常执行,我们通常应该使用click方法。

但有的时候,我们难以用通常的方法定位元素, 可以用这个tap方法,根据坐标来点击。既然tap是用坐标来点击界面的,我们怎么知道这个元素的坐标呢?大家还记得用inspect 查看该元素的属性中,有一个 bounds 属性吗?它就是表示元素的左上角,右下角坐标的 坐标。我们还可以使用 UIAutomatorviewer 直接光标移动,看右边的属性提示。

tap 方法可以像这样进行调用

driver.tap([(850,1080)],300)

它 有 两个参数:

  • 第一个参数是个列表,表示点击的坐标。

    注意最多可以有5个元素,代表5根手指点击5个坐标。所以是list类型。

    如果我们只要模拟一根手指点击屏幕,list中只要一个元素就可以了

  • 第二个参数 表示 tap点按屏幕后 停留的时间。

    如果点按时间过长,就变成了长按操作了。

输入

最常见的操作之一, 使用 WebElement 对象的 send_keys 方法

获取界面文本信息

可以通过 WebElement 对象的 .text 属性获取该对象的文本信息

滑动

我们做移动app测试的时候,经常需要滑动界面。

怎么模拟滑动呢? WebDriver对象的 swipe方法,就提供了这个功能

比如

driver.swipe(start_x=x, start_y=y1, end_x=x, end_y=y2, duration=800)

前面4个参数 是 滑动起点 和 终点 的x、y坐标。

第5个参数 duration是滑动从起点到终点坐标所耗费的时间。

注意这个时间非常重要,在屏幕上滑动同样的距离,如果时间设置的很短,就是快速的滑动。

比如:一个翻动新闻的界面,快速的滑动,就会是扫动的动作,会导致内容 惯性 滚动很多。

按键

前面的示例代码中已经使用过 调用 press_keycode 方法,就能模拟 按键动作,包括安卓手机的实体按键和 键盘按钮。

如下代码所示

from appium.webdriver.extensions.android.nativekey import AndroidKey

# 输入回车键,确定搜索
driver.press_keycode(AndroidKey.ENTER)

按键的定义,可以参考这篇文档 https://github.com/appium/python-client/blob/master/appium/webdriver/extensions/android/nativekey.py

长按、双击、移动

Appium的 TouchAction 类提供了更多的手机操作方法,比如:长按、双击、移动

参考源代码中的注释 https://github.com/appium/python-client/blob/master/appium/webdriver/common/touch_action.py

比如,下面就是一个长按的例子

from appium.webdriver.common.touch_action import TouchAction
# ...
actions = TouchAction(driver)
actions.long_press(element)
actions.perform()

查看通知栏

  • 打开通知栏

    安卓手机, 查看通知栏的动作可以是从屏幕顶端下滑来查看通知。

    我们刚刚学过滑动,感兴趣的朋友可以自己试试,关键是要找对滑动的起始点和滑动距离。

    更方便的,我们可以使用如下代码,直接打开通知栏

    driver.open_notifications()
    

    通知栏里面的元素,自动化的方法 和 前面介绍的App界面元素自动化是一样的。

  • 收起通知栏

    收起通知栏,可以使用前面介绍的模拟按键, 发出返回按键 的方法。

adb 命令

这个adb 在 sdk的 platform-tools 目录下面, 确保路径在path环境变量中。

可以使用 Python的 os.system() 或者 subprocess 来自动化调用它,完成我们的各种自动化需求。

比如,我们自动化过程中,可能需要截屏手机,并且下载到指定目录中,就可以在我们的Python程序中这样写

import os
os.system('adb shell screencap /sdcard/screen3.png && adb pull /sdcard/screen3.png')

特别是,还可以通过adb 使用 am(activity manager) 和pm (package manager) 两个工具, 可以启动 Activity、强行停止进程、广播 intent、修改设备屏幕属性、列出应用、卸载应用等。

点击这里查看官方文档中介绍的adb命令

查看连接的设备

adb devices -l

列出文件和传输文件

  • 查看目录
adb shell ls /sdcard
  • 上传
adb push wv.apk /sdcard/wv.apk
  • 下载
adb pull /sdcard/new.txt

截屏

adb shell screencap /sdcard/screen.png

截屏后的文件存在手机上,可以使用 adb pull 下载下来

shell

登录到手机设备上shell的进行访问,就像远程登录一样,可用来在连接的设备上运行各种命令。

大家可以 执行一下 adb shell 然后执行各种 安卓支持的 Linux命令,比如 ps、netstat、netstat -an|grep 4724、 pwd、 ls 、cd 、rm 等。

执行quit退出 shell

四 unittest测试框架

基本代码

  • 导包 :

    from appium import webdriver
    from time import sleep
    import  unittest
    
  • 创建全局变量 driver

    • driver=None 放在类之外
  • 创建测试类

    • class 类名(unittest.TestCase)
  • 创建初始化和还原环境的函数

    @classmethod
    def setUpClass(cls):
        # 所有测试函数运行前运行一次
        device = {}
        device={}
        device['deviceName']='192.168.120.102:5555'
        device['platformName']='Android'
        device['platformVersion']='9'
        device['noReset']=True
        device['appPackage']='com.android.quicksearchbox'
        device['appActivity']='com.android.quicksearchbox.SearchActivity'
        device['unicodeKeyboard']= True
        device['resetKeyboard']=True
        global driver  # 需要声明全局变量
    @classmethod
    def tearDownClass(cls):
        所有测试幻术运行完后运行一次
        # 关闭app
        driver.quit()
    
  • 测试函数

    def test函数名(self):  # 函数名必须以test开头
        u"测试描述"  # 省略此行时显示函数名
        global driver
        测试代码
    # 多个测试函数按照函数名的ASSCII顺序执行
    
  • 运行测试

    if __name__ == '__main__':
        unittest.main(verbosity=2)
        # verbosity=2 表示显示每个用例的详细信息,可以省略,但测试结果不详细  
    
  • 测试结果

    测试结果
        .:代表测试通过。
        F:代表测试失败,F 代表 failure。
        E:代表测试出错,E 代表 error。
        s:代表跳过该测试,s 代表 skip。
    
  • 百度搜索测试示例

    from appium import webdriver
    from time import sleep
    import unittest
    
    driver = None
    
    
    class Webapp(unittest.TestCase):
        # 创建初始化和还原环境的函数
        @classmethod
        def setUpClass(cls):
            # 所有测试函数运行前运行一次
            # 指定设备信息
            device = {}
            device["deviceName"] = "emulator-5554"
            device["platformName"] = "Android"
            device["platformVersion"] = "7"
            device["appPackage"] = "com.android.browser"
            device["appActivity"] = "BrowserActivity"
            device["noReset"] = True
            global driver
            driver = webdriver.Remote("http://localhost:4723/wd/hub", device)
    
        @classmethod
        def tearDownClass(cls):
            # 所有测试函数运存完后运行一次
            # 关闭app
            global driver
            driver.quit()
    
        # 测试函数
        def testBaidu(self):
            # 操作元素
            u"测试百度搜索"
            global driver
            driver.find_element_by_id("com.android.browser:id/url").clear()
            driver.find_element_by_id("com.android.browser:id/url").click()
            driver.find_element_by_id("com.android.browser:id/url").send_keys("https://www.baidu.com")
            driver.press_keycode(66)
            sleep(1)
            driver.find_element_by_xpath(
                '/hierarchy/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout[2]/android.widget.LinearLayout/android.widget.FrameLayout/android.webkit.WebView/android.webkit.WebView/android.view.View/android.view.View[1]/android.view.View/android.view.View[2]/android.view.View[3]/android.widget.EditText').send_keys(
                "Python学习")
            driver.find_element_by_xpath(
                '/hierarchy/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout[2]/android.widget.LinearLayout/android.widget.FrameLayout/android.webkit.WebView/android.webkit.WebView/android.view.View/android.view.View[1]/android.view.View/android.view.View[2]/android.view.View[3]/android.widget.EditText').click()
    
            driver.press_keycode(66)
            sleep(3)
    
    
    if __name__ == '__main__':
        unittest.main(verbosity=2)
    

断言

设定断言方式一 :
	assert str1 in str2,断言失败时的消息文本
设定断言方式二 :
	assert 表达式 1==表达式 2
设置断言方式三 :
	self.assertIn(str1,str2)  # 断言 str1 是否在 str2 中
设置断言方式四 :	
	self.assertEqual(实际,预期)
WebApp  中的断言:
	driver.page_source # 获得网页返回结果/所有页面元素

参数化

  • 安装 nose_parameterized : pip install nose_parameterized

  • 导入包parameterized

    • import parameterized
  • 定义参数

    将参数放在了列表中
    data = [
        [数据11,数据12],
        [数据21,数据22],
        ...
    ]
    
  • 引用参数

    @parameterized.parameterized.expand(data):  # data的数据一行一行获取
    def test函数名(self,参数1,参数2,...):
    

Python+appium app自动化_第5张图片

测试报告

  • 导包 : import HTMLTestRunner

  • 创建测试套件

    suit = unittest.TestSuite()
    tests = unittest.defaultTestLoader.discover("测试模块的目录, pattern="test_*.py") 
                     # pattern="test_*.py" 指的是目录下所有test_开头的py文件都运存并生成测试报告
    suite.addTest(tests)
    
  • 运行测试报告,保存报告

    now = time.strftime("%Y%m%d %H%M%S", time.localtime())  # 获取当前时间并设置格式
    reportFile = "./" + now + "_result.html"  # 生成报告的路径及文件名
    fp = open(reportFile, "w",encoding="utf-8")
    runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title="功能测试", description="报告说明与描述,")
    runner.run(suite)
    fp.close()
    

五 安装APK

  1. 安装 Genymotion-ARM-Translation

    Genymotion 是一款基于 x86 架构的 Android 模拟器,而大部分应用是 arm 架构,所以
    两者不兼容。
    	对于 x86 的真机,X86 处理器已经能够基本兼容 ARM 了指令。
    	对于 Genymotion 模拟器,则通过安装 Genymotion-ARM-Translation 来进行兼容。
    genymotion 使用 Genymotion-ARM-Translation.zip 文件是不通用的,每个 Android 版本
    对应 ARM 指令集的版本也是不一样。
    	1. 将文件复制到模拟器
    		adb push Genymotion_ARM_Translation.zip /sdcard/Download
    	2. 将 Genymotion-ARM-Translation.zip 继续拖动到模拟器中
    	3. 重启模拟器 :adb reboot
    	4. 如果拖动没安装成功,可以使用下面的命令安装
    		adb shell
    		cd /sdcard/Download/
    		sh /system/bin/flash-archive.sh /sdcard/Download/Genymotion_ARM_Translation.zip
    
  2. 安装apk

    1. 命令安装 : adb install apk文件名
    2. 拖动安装
  3. 操作app权限弹窗

    显示当前app的包名及界面的Activity
    adb shell dumpsys window w | findstr \/ | findstr name=
    
    toast=('xpath','//*[contains(@text,"ALLOW")]')
    	查找 xpath 路径,标记任意,text 属性中包括 ALLOW
    	id 有时候会发生变化
    t=WebDriverWait(driver,10,0.1).until(expected_conditions.presence_of_element_located(toa
    st))
    	寻找指定的 toast 元素
    	最大超时时间 10s,默认 30s,间隔查询时间 0.1s,默认 0.5s 查询一次
    	需要导入包、
    	from selenium.webdriver.support.ui import WebDriverWait
    	等待模块
    	from selenium.webdriver.support import expected_conditions
    	判断模块
    t.click()  单击找到的元素
    
  4. 滑动

    • 获取屏幕分辨率尺寸

      width = driver.get_windows_size()['width']
      height = driver.get_windows_size()['height']
      
      for i in range(1,7):
      	driver.swipe(width/2,height*3/4,width/2,height/4,1000)
      向上滑,每次间隔 1000ms
      

    判断是否出现指定窗口

    第一种 :
        if driver.current_activity()=='activity 名称' :  
        # driver.current_activity():获得当前 activity 名称
            适当提前延时
    第二种 :
        if driver.wait_activity("activity 名称",超时时间)
    	返回值 :窗口存在是返回 True
    
  • 其他

    • device['autoLaunch] = False

      • 有时不想让appium每次都自动启动app, 而是想自己打开activity或者在原来activity基础上继续操作,可以方便使用此项

      • 但是device[‘appPackage’]和device[‘appActivity’]不能省略

      • 启动app

        driver.launch_app()

      • 关闭app

        driver.close_app()

      • 重置app

        driver.reset() : 清除设置以后启动或重启 App

      • 自己启动 activity

        driver.start_activity('包名','Activity页面名')

      • 在已经打开的Activity页面继续操作

    • 查看当前的App类型/上下文

      driver.contexts

    • 查看当前的app类型/上下文

      driver.current_context

    • 切换到指定app类型/上下文

ndows_size()[‘width’]
height = driver.get_windows_size()[‘height’]
```

 ```
 for i in range(1,7):
 	driver.swipe(width/2,height*3/4,width/2,height/4,1000)
 向上滑,每次间隔 1000ms
 ```

判断是否出现指定窗口

第一种 :
    if driver.current_activity()=='activity 名称' :  
    # driver.current_activity():获得当前 activity 名称
        适当提前延时
第二种 :
    if driver.wait_activity("activity 名称",超时时间)
	返回值 :窗口存在是返回 True
  • 其他

    • device['autoLaunch] = False

      • 有时不想让appium每次都自动启动app, 而是想自己打开activity或者在原来activity基础上继续操作,可以方便使用此项

      • 但是device[‘appPackage’]和device[‘appActivity’]不能省略

      • 启动app

        driver.launch_app()

      • 关闭app

        driver.close_app()

      • 重置app

        driver.reset() : 清除设置以后启动或重启 App

      • 自己启动 activity

        driver.start_activity('包名','Activity页面名')

      • 在已经打开的Activity页面继续操作

    • 查看当前的App类型/上下文

      driver.contexts

    • 查看当前的app类型/上下文

      driver.current_context

    • 切换到指定app类型/上下文

      driver.switch_to.context("app名")

你可能感兴趣的:(软件测试)