用户端APP自动化测试_L2

2023-11-04

目录:

  1. appium server 环境安装
  2. capability 进阶用法
  3. 元素定位工具
  4. 高级定位技巧-xpath 定位
  5. 高级定位技巧-css 定位与原生定位
  6. 特殊控件 toast 识别
  7. 显式等待高级使用
  8. 高级控件交互方法
  9. 设备交互api
  10. 模拟器控制
  11. 雪球财经app股票详情功能点自动化测试实战

1.appium server 环境安装

  • 安装 Node.js
  • 安装 Appium Server
  • 安装 Appium Doctor

安装 Node.js

  • 下载
  • 默认配置环境变量
  • 查看源
  • 更换源
// 查看源
npm config get registry

//更换源
npm config set registry https://registry.npm.taobao.org

安装成功后的截图:

安装 Appium Server

  • Appium Server 安装
// 安装最新版本的appium server
npm install -g appium


// 注意 -g 参数一定要有,不能省,代表全局
// 安装某个特定的版本
npm install -g appium@1.19.1

 安装环境检测工具

  • 运行环境检测工具 appium-doctor
# 安装检测工具 appium-doctor
npm install -g appium-doctor
  • 执行检测命令 appium-doctor

运行 Appium 服务

// 帮助文档
appium --help

// 运行 appium 服务
appium

2.capability 进阶用法

deviceName

  • 只是设备的名字,别名
  • 随便起
  • 不能锁定唯一一个设备

uid

  • 多设备选择的时候,要指定 uid

  • 默认读取设备列表的第一个设备

  • 设备列表获取

 adb devices

newCommandTimeout

  • appium 程序应等待来自客户端的新命令多长时间
  • 超时后会话删除
  • 默认 60 秒
  • 设置为 0 禁用

 autoGrantPermissions

  • 授予启动的应用程序某些权限
  • 默认为 false
  • 发生任何错误,强制服务器将实际的 XML 页面源转储到日志中.

 测试策略

  • noReset
  • fullReset
  • dontStopAppOnReset

noReset

  • 默认为false
  • 安卓true
    • 不停止应用程序
    • 不清除应用程序数据
    • 不卸载 apk

 fullReset

  • 默认为 falsetrue:新会话之前完全卸载被测应用程序

  • 安卓

    • 在会话开始之前(appium 启动 app)和测试之后停止应用程序
    • 清除应用程序数据并卸载 apk

dontStopAppOnReset

  • 默认为 false
  • 不希望应用程序在运行时重新启动,设置为 true

 #打开的app退出后重新启动

adb shell am start -S 包名/activity名

#打开的app不需要退出,直接使用当前页面

adb shell am start 包名/activity名

性能相关

  1. skipServerInstallation:这个选项可以跳过服务器的安装过程,如果在测试过程中不需要进行服务器安装,就可以使用这个选项来提高测试效率。
  2. skipDeviceInitialization:这个选项可以跳过设备的初始化过程,如果在测试过程中设备已经初始化过,就可以使用这个选项来避免重复初始化,提高测试效率。
  3. skipUnlock:这个选项可以跳过设备的解锁过程,如果在测试过程中设备已经解锁,就可以使用这个选项来避免重复解锁,提高测试效率。
  4. skipLogcatCapture:这个选项可以跳过日志的获取过程,如果在测试过程中不需要日志获取,就可以使用这个选项来提高测试效率。
  5. systemPort:这个选项可以获取系统的端口号,在测试过程中需要访问系统端口时使用。
  6. ignoreUnimportantViews:这个选项可以跳过不重要组件的获取过程,如果在测试过程中不需要获取这些组件的信息,就可以使用这个选项来提高测试效率。
  7. relaxed-security:这个选项可以在启动时设置放松的安全性,以允许某些应用程序在安全性检查中运行,从而提高测试效率。但是,这个选项可能会增加安全风险,应该谨慎使用。

3.元素定位工具

uiautomatorviewer 工具安装(不推荐)

  • 工具的安装:Android SDK 自带的界面分析工具
  • 打开 tools/bin 目录下的 uiautomatorviewer 程序

uiautomatorviewer 工具功能介绍

  • 第一个是通过分析给定的文件定位
  • 第二个是将当前界面截图并分析xml结构
  • 第三个与第二个功能类似,但它会对页面内容进行压缩,导致一些控件定位不准确
  • 第四个是保存当前界面的截图以及xml结构

weditor 安装与运行

  • 要求:python 3.6+ 以上
  • 安装:weditor 是 python 的第三方库
    • pip install weditor 进行安装
    • 运行:安装完成之后,在命令行运行 python -m weditor 即可

weditor 功能介绍

  • 支持 Android 和 iOS 的界面分析
  • 通过设备的 uuid 连接设备
  • 展示页面结构

 appium inspector 安装与运行

  • 下载:下载链接
  • 运行 appium inspector
    • 运行 appium server
    • 本地的 adb 已连接设备
    • Desired Capabilities 参数填写
  • 点击 Start Session
  • 注意:在运行appium inspect 的时候需要检查一下weditor在手机上安装的ATX服务是否停止,因为ATX服务会占用uiautomator2的

appium inspector 功能简介

  • 定位的模式相关
    • 原生app模式
    • 混合模式
  • 界面操作相关
    • 选择元素
    • 坐标滑动界面
    • 坐标点击元素
  • 其他功能
    • 返回
    • 刷新页面
    • 录制
    • 搜索元素
    • 复制 xml 结构

 对选中元素操作

  • 点击
  • 输入
  • 清空
  • 复制元素的属性
  • 获取元素的响应时间
  • 元素的属性

4.高级定位技巧-xpath 定位

xpath 函数

包含-contains()

  • Xpath 表达式中的一个函数

  • contains()函数匹配==属性值==中包含的==字符串==

    //*[contains(@属性,"属性值")]

xpath 轴

父子-当前节点的父节点

//*[@text="HK"]/..

//*[@text="HK"]/parent::*

父子-当前节点的子节点 

//*[@resource-id="com.xueqiu.android:id/stock_layout"]/child::*

爷孙-当前节点的爷爷

//*[@text="HK"]/../..

//*[@text="HK"]/parent::*/parent::*

爷孙-当前节点的孙子

//*[@resource-id="com.xueqiu.android:id/stock_layout"]/child::*/child::*

祖先-ancestor

  • 返回当前节点的所有祖先

    //*[@text="HK"]/ancestor::android.widget.RelativeLayout
  • 显式指定要返回的祖先

    //*[@text="HK"]/ancestor::android.widget.RelativeLayout[1]

 兄弟姐妹-sibling

  • 节点后的兄弟姐妹节点,following-sibling
  • 节点前的兄弟姐妹节点,preceding-sibling

following-sibling

  • 选择当前节点之后的所有兄弟节点

  • 节点后有一个兄弟节点

    //*[@text="HK"]/following-sibling::*
  • 节点后有多个兄弟节点

    //*[@resource-id="com.xueqiu.android:id/stock_layout"]/following-sibling::*[@resource-id="com.xueqiu.android:id/price_layout

 preceding-sibling

  • 选择当前节点之前的所有兄弟节点

  • 节点前有一个兄弟节点

    //*[@text="09988"]/preceding-sibling::*
  • 节点前有多个兄弟节点

    //*[@resource-id="com.xueqiu.android:id/add_attention"]/preceding-sibling::*[@resource-id="com.xueqiu.android:id/price_layou

XPath 运算符

 AND

  • 可以在 XPath 表达式中放置 2 个条件

  • 在 AND 两个条件都应该为真的情况下,才能找到元素

    //*[@resource-id="com.xueqiu.android:id/current_price" and @text="107.8"]

OR

  • 可以在 XPath 表达式中放置 2 个条件

  • 在 OR 的情况下,两个条件中的任何一个为真,就可找到元素。

  • OR 定位获取的是并集

    //*[@resource-id="com.xueqiu.android:id/tv_stock_add_follow" or @text="加自选"]

5.高级定位技巧-css 定位与原生定位

原生定位

# ID 定位
driver.find_element_by_android_uiautomator('new UiSelector().resourceId("<element-ID>")')

# 组合定位
driver.find_element_by_android_uiautomator('new UiSelector().resourceId("com.xueqiu.android:id/tab_name").text("我的")')

 css selector 定位介绍

  • 官网说明
  • Android: Appium Server 版本 >= 1.19.0
  • iOS:Appium Server>= 1.21.0
代码:
driver.find_element(AppiumBy.CSS_SELECTOR,"#com\.xueqiu\.android\:id\/tv_search")

解析前:
{"using":"css selector","value":"#com\\.xueqiu\\.android\\:id\\/tv_search"}

解析后:
{"strategy":"-android uiautomator","selector":"new UiSelector().resourceId(\"com.xueqiu.android:id/tv_search\")",...}

 css selector 用法

# 通过 id
elementById("someResourceID")`
    -> `elementsByCss("#someResourceID")
# 通过 class
elementsByClassName("android.widget.TextView")`
    -> `elementsByCss("android.widget.TextView")
# 通过 accessibility id
elementsByAccessibilityId("Some Content Description")`
    -> `elementsByCss('*[description="Some Content Description"]')
# 通过 xpath
elementsByXpath("//android.widget.TextView[@description='Accessibility']")`
    -> `elementsByCss("android.widget.TextView[description='Accessibility']")

iOS css selector 定位

  • Appium Server 版本>=1.21.0
  • 官网:https://github.com/appium/appium/releases/tag/v1.21.0

总结

  • Appium Server 版本>=1.21.0
  • css selector 会转化为 Android/iOS 原生定位的定位策略
  • Android 转为 Android Uiautomator 定位方式
  • iOS 转为 class chain 定位方式

6.特殊控件 toast 识别

Toast 是什么

  • 一种消息框类型

  • 永远不会获得焦点,无法被点击

  • Toast显示的时间有限,Toast会根据用户设置的显示时间后自动消失

  • 是系统级别的控件,属于系统settings

  • Toast类的思想:就是尽可能不引人注意,同时还向用户显示信息,希望他们看到

Toast 定位

  • appium 用的是uiautomator底层来抓取toast,
  • 再把toast放到控件树内,但是它本身不属于空间
  • 使用的是uiautomator2

 Toast 定位

  • xpath 可以找到 ``` //*[@class="android.widget.Toast"]

    //*[contains(@text,"xxx")] ``` xxx:toast的文本内容

    driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(50));  
    driver.findElement(AppiumBy.xpath("//*[@class=\"android.widget.Toast\"]"));

代码示例:

from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy


class TestToast():
    def setup(self):
        desire = {
            'platformName': 'android',
            'platformVersion': '6.0',
            'deviceName': 'emulator-5554',
            'appPackage': 'io.appium.android.apis',
            'appActivity': 'io.appium.android.apis.view.PopupMenu1',
            'automationName': 'uiautomator2'
        }
        self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desire)
        self.driver.implicitly_wait(5)

    def teardown(self):
        self.driver.quit()

    def test_toast(self):
        self.driver.find_element(MobileBy.ACCESSIBILITY_ID, "Make a Popup!").click()
        self.driver.find_element(MobileBy.XPATH, "//*[@text='Search']").click()
        print(self.driver.find_element(MobileBy.XPATH, "//*[@class='android.widget.Toast']").text)
        # print(self.driver.find_element(MobileBy.XPATH, "//*[contains(@text, 'Clicked popup')]").text)

7.显式等待高级使用

Wait 等待

  • 强制等待:sleep 不推荐
  • 全局隐式等待
    • 在服务端等待
    • driver.implicitly_wait(TIMEOUT)
  • 显式等待
    • 在客户端等待
    • WebDriverWait(self.driver,10,0.5).until(expected_conditions.visibility_of_element_located(LOCATOR))

显式等待

  • 显式等待
    • 显示等待与隐式等待相对,显示等待必须在每个需要等待的元素前面进行声明。
    • 是针对于某个特定的元素设置的等待时间,在设置时间内,默认每隔一段时间检测一次当前页面某个元素是否存在,
    • 如果在规定的时间内找到了元素,则直接执行,即找到元素就执行相关操作,
    • 如果超过设置时间检测不到则抛出异常。默认检测频率为 0.5s,默认抛出异常为:NoSuchElementException
  • 显示等待用到的两个类:
    • WebDriverWait 和 expected_conditions 两个类

显式等待

  • 显式等待可以等待动态加载的 ajax 元素,显式等待需要使 ExpectedCondtions 来检查条件
  • 一般页面上元素的呈现
    • title 出现 首先出现 title
    • dom 树出现 presence ,还不完整
    • css 出现 (可见 visibility)
    • js 出现,js 特效执行 ( 可点击 clickable)
  • html 文档是自上而下加载的,
  • js 文件加载会阻塞 Html 内容的加载,有些 JS 异步加载的方式来完成 js 的加载
  • 样式表下载完成之后会跟之前的样式表一起进行解析,会对之前的元素重新渲染

WebDriverWait 用法

  • WebDriverWait 用法
    • WebDriverWait(driver,timeout,poll_frequency=0.5,ignored_exceptions=None)
    • driver:浏览器驱动
    • timeout:最长超时时间,默认以秒为单位
    • poll_frequency:检测的间隔步长,默认为 0.5s
    • ignored_exceptions:超时后的抛出的异常信息,默认抛出 NoSuchElementExeception 异常。
  • WebDriverWait 的 until()和 until_not()方法:
    • method: 在等待期间,每隔一段时间(init中的 poll_frequency)调用这个传入的方法,直到返回值不是 False , message: 如果超时,抛出 TimeoutException,将 message 传入异常
    • until_not: 与 until 相反,until 是当某元素出现或什么条件成立则继续执行,until_not 是当某元素消失或什么条件不成立则继续执行,参数也相同。

expected_conditions 类

  • presence_of_element_located 判断元素是否被加到了 DOM 树里,并不代表该元素一定可见
    • 用法:WebDriverWait().until(expected_conditions.presence_of_element_located(元素对象))
  • visibility_of_element_located 判断某个元素是否可见,可见代表元素非隐藏,并且元素的宽和高都不等于 0
    • 用法:
    • WebDriverWait().until(expected_conditions.visibility_of_element_located(元素定位符))

使用 lambda 表达式

  • WebDriverWait(driver,time).until(lambda x:x.find_element_by_id("someId")
  • 返回一个元素

总结三种等待方式

  • 隐式等待,尽量默认都加上,时间限定在 3-6s,不要太长, 为了所有的 find_element 方法都有一个很好的缓冲
  • 显式等待,用来处理隐式等待无法解决的一些问题,比如:文件上传(可以设置长一点),文件上传需要设置 20s 以上, 但是如果设置隐式等待, 它会在每个 find 方法都等这么长时间 , 一旦发现没有找到元素, 就会等 20s 以后才抛出异常, 影响 case 的执行效率,这时候就需要用显式等待,显式等待可以设置的长一点
  • 强制等待:一般不推荐,前两种基本能解决绝大部分问题,如果某个控件没有任何特征,只能强制等待,这种情况比较少

8.高级控件交互方法

Actions

  • Actions
  • 执行一系列或多个键盘和指针(触摸、鼠标、触控笔)操作链
  • w3c

W3C 事件流 

用法

  • 定义 ActionChains 实例
  • 定义输入源
  • 定义动作

 代码

...
# 定义ActionChains 实例
actions = ActionChains(driver)
# 第一步:定义输入源
# ActionChains里有个属性是ActionBuilder类型的, 使用的就是w3c协议
# 可以定义鼠标指针源,键盘源,滚轮源事件
actions.w3c_actions = ActionBuilder(driver, mouse=PointerInput(interaction.POINTER_TOUCH, "touch"))
# 第二步:定义动作
# 移动到起点-> 按下-> 滑动-> 抬起
actions.w3c_actions.pointer_action.move_to_location(115, 183)
actions.w3c_actions.pointer_action.pointer_down()
actions.w3c_actions.pointer_action.move_to_location(362, 179)
actions.w3c_actions.pointer_action.release()
actions.perform()
...

练习

  • 安装手势密码锁 app(TouchAction.apk)
  • 打开应用
  • 点击【设置手势】
  • 完成手势操作(如图)

 

代码示例:

import time
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver import ActionChains
from selenium.webdriver.common.actions import interaction
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver.common.actions.pointer_input import PointerInput


class TestAction:
    def setup(self):
        caps = {}
        caps["platformName"] = "Android"
        caps["appPackage"] = "cn.kmob.screenfingermovelock"
        caps["appActivity"] = "com.samsung.ui.FlashActivity"
        caps["deviceName"] = "127.0.0.1:7555"
        caps["ensureWebviewsHavePages"] = True
        caps["noReset"] = "true"
        self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
        self.driver.implicitly_wait(15)

    def teardown(self):
        self.driver.quit()

    def test_action(self):
        el1 = self.driver.find_element(AppiumBy.ID,"cn.kmob.screenfingermovelock:id/patternTxt")
        el1.click()
        # 定义ActionChains 实例
        actions = ActionChains(self.driver)
        # 第一步:定义输入源
        # ActionChains里有个属性是ActionBuilder类型的, 使用的就是w3c协议
        # 可以定义鼠标指针源,键盘源,滚轮源事件
        actions.w3c_actions = ActionBuilder(self.driver, mouse=PointerInput(interaction.POINTER_TOUCH, "touch"))
        # 第二步:定义动作
        # 移动到起点-> 按下-> 滑动-> 抬起
        actions.w3c_actions.pointer_action.move_to_location(118, 176)
        actions.w3c_actions.pointer_action.pointer_down()
        actions.w3c_actions.pointer_action.move_to_location(355, 176)
        actions.w3c_actions.pointer_action.pause(0.5)
        actions.w3c_actions.pointer_action.move_to_location(751, 221)
        actions.w3c_actions.pointer_action.pause(0.5)
        actions.w3c_actions.pointer_action.move_to_location(752, 518)
        actions.w3c_actions.pointer_action.pause(0.5)
        actions.w3c_actions.pointer_action.move_to_location(751, 821)
        actions.w3c_actions.pointer_action.release()
        actions.perform()
        time.sleep(2)

9.设备交互api

常用的设备交互命令(建议去appium官网查看文档)

  • 模拟电话、短信
  • 网络模式
  • 横竖屏切换
  • App处理
  • 录屏

模拟电话、短信

  • appium可以模拟来电话,来短信功能,在app运行过程中收到短信/电话,app如何做处理的,专属的一些场景
  • 只支持原生模拟器,不支持mumu,genimotion等
driver.makeGsmCall(PHONE_NUMBER, GsmCallActions.CALL);
driver.makeGsmCall(PHONE_NUMBER, GsmCallActions.ACCEPT);
driver.makeGsmCall(PHONE_NUMBER, GsmCallActions.CANCEL);
driver.sendSMS("555-123-4567", “Appium Test”);

网络设置

 def set_network_connection(self, connection_type: int) -> int:
        """Sets the network connection type. Android only.

        Possible values:

            +--------------------+------+------+---------------+
            | Value (Alias)      | Data | Wifi | Airplane Mode |
            +====================+======+======+===============+
            | 0 (None)           | 0    | 0    | 0             |
            +--------------------+------+------+---------------+
            | 1 (Airplane Mode)  | 0    | 0    | 1             |
            +--------------------+------+------+---------------+
            | 2 (Wifi only)      | 0    | 1    | 0             |
            +--------------------+------+------+---------------+
            | 4 (Data only)      | 1    | 0    | 0             |
            +--------------------+------+------+---------------+
            | 6 (All network on) | 1    | 1    | 0             |
            +--------------------+------+------+---------------+
self.driver.set_network_connection(1)
self.driver.set_network_connection(4)

横竖屏切换

  • 横竖屏切换,官方暂不支持python,以下是java代码。
  • 切换成横屏
    • driver.rotate(Screenorientation.LANDSCAPE)
  • 切换成竖屏
    • driver.rotate(Screenorientation.PORTRAIT)

 获取日志

self.driver.log_types
self.driver.get_log("logcat")

其它常用操作

  • 锁屏
    • driver.lock()
  • 截图
    • driver.get_screenshot_as_file('./photos/img.png')
  • 录屏:模拟器需要 androidAPI>27,华为不支持,只支持 8.0以上的版本
    • 开始录制:self.driver.start_recording_screen()
    • 结束录制:self.driver.stop_recording_screen()

 代码示例:

import time
from appium import webdriver
from appium.webdriver.extensions.android.gsm import GsmCallActions


class TestDevice:
    def setup(self):
        caps = {}
        caps["platformName"] = "Android"
        caps['platformVersion'] = '6.0'
        caps["appPackage"] = "cn.kmob.screenfingermovelock"
        caps["appActivity"] = "com.samsung.ui.FlashActivity"
        caps["deviceName"] = "127.0.0.1:7555"
        caps["ensureWebviewsHavePages"] = True
        caps["noReset"] = "true"
        caps['unicodeKeyBoard'] = 'true'
        caps['resetKeyBoard'] = 'true'
        self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
        self.driver.implicitly_wait(15)

    def teardown(self):
        self.driver.quit()

    def test_mobile(self):
        self.driver.make_gsm_call('15033657989', GsmCallActions.CALL)
        self.driver.send_sms('15033657956', 'hello appium api')
        time.sleep(5)
        self.driver.set_network_connection(1)
        time.sleep(2)
        self.driver.set_network_connection(4)
        time.sleep(2)
        self.driver.get_screenshot_as_file('./datas/screenshot/img.png')

10.模拟器控制

android 模拟器创建

  • Android Studio
  • 在命令行启动模拟器
    • emulator -list-avds 模拟器列表
    • emulator ‘@foo’ or ‘-avd foo’

配置

  • desirecap里面需要配置
    • avd: ‘模拟器名’
  • 注意自动启动模拟器,只能是sdk的模拟器,第三方模拟器不支持,7.0不支持

代码示例:

test_simulator_control.py

import time

from appium import webdriver


class TestControl:
    def setup(self):
        caps = {}
        caps["platformName"] = "Android"
        caps['platformVersion'] = '6.0'
        caps["appPackage"] = "cn.kmob.screenfingermovelock"
        caps["appActivity"] = "com.samsung.ui.FlashActivity"
        caps["deviceName"] = "127.0.0.1:7555"
        caps["ensureWebviewsHavePages"] = True
        caps['unicodeKeyBoard'] = 'true'
        caps['resetKeyBoard'] = 'true'
        caps["noReset"] = "true"
        caps['avd'] = 'Resizable_Experimental_API_34'
        self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
        self.driver.implicitly_wait(15)

    def teardown(self):
        self.driver.quit()

    def test_control(self):
        self.driver.set_network_connection(1)
        time.sleep(2)
        self.driver.set_network_connection(4)
        time.sleep(2)
        self.driver.get_screenshot_as_file('./datas/screenshot/img.png')

 11.雪球财经app股票详情功能点自动化测试实战

产品分析

  • 被测应用:雪球 App
  • 雪球 App 介绍
    • 雪球 app 是一款免费版,非常实用的股票平台,提供热点资讯内容和实时行情,提供了证券交流交易,公募、理财服务
    • 雪球 app 功能包括搜索股票,查看行情,交易,浏览热门文章,发帖,登录,注册,等功能

Page Object 模式六大原则

总结

  • 公共方法表示该页提供的服务。
  • 尽量不要暴露页面的内部
  • 一般不要断言
  • 方法返回其他页面对象
  • 不需要表示整个页面
  • 同一行动的不同结果用不同的方法建模

用例设计

  • 搜索场景
    • 打开【雪球】应用首页
    • 点击搜索框,进入搜索页面
    • 向搜索输入框中输入【alibaba】
    • 点击搜索结果中的【阿里巴巴】
    • 切换到 tab 的【股票】
    • 找到 股票【阿里巴巴】的股票价格 price
    • 判断 price 在 110 上下 10%浮动

实战练习思路

 

优化用例-封装样板代码(基类)

  • 问题:
    • 直接调用 Appium API,导致存在大量的样板代码,find、finds 等。
  • 解决方案:
    • 常用的 UI 操作封装在 base_page 中。
    • 日志
    • 截图
    • page_source

 优化用例-页面元素提取

  • 问题:
    • 页面定位写在每个 Page 的方法中,如果此页面定位存在多处复用,那么需要多处修改
  • 解决方案:
    • 将页面定位抽离为私有类变量,符合六大原则中不要暴露页面内部的元素给外部

优化用例-复用driver

  • 问题
    • 当执行多条测试用例时,每次执行用例都创建一个driver,浪费资源,效率低
    • 当执行多条测试用例时,所有的用例都使用同一个driver ,一旦服务出现问题(比如socket hang up)服务宕了,会导致后面所有的用例执行失败
  • 解决方案
    • 当创建driver 时,做个判断,如果driver 为None,则创建,否则,复用之前的driver

代码示例:

base/base_page.py

from appium.webdriver.webdriver import WebDriver


class BasePage:
    def __init__(self, driver: WebDriver = None):
        self.driver = driver

    def find(self, by, locator):
        return self.driver.find_element(by, locator)

    def find_and_click(self, by, locator):
        self.find(by, locator).click()

    def find_and_send(self, by, locator, text):
        self.find(by, locator).send_keys(text)

    def find_and_gettext(self, by, locator):
        return self.find(by, locator).text

base/xueqiu_app.py

from appium import webdriver

from app_automation_testing.test_xueqiu_po.base.base_page import BasePage


class XueQiuApp(BasePage):
    def start(self):
        if self.driver == None:
            print("driver == None")
            caps = {}
            caps["platformName"] = "Android"
            caps['platformVersion'] = '6.0'
            caps["appPackage"] = "com.xueqiu.android"
            caps["appActivity"] = ".view.WelcomeActivityAlias"
            caps["deviceName"] = "127.0.0.1:7555  device"
            caps["noReset"] = "true"
            self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
            self.driver.implicitly_wait(10)
        else:
            print("复用driver")
            self.driver.launch_app()
        return self

    def restart(self):
        pass

    def stop(self):
        self.driver.quit()

    def goto_main(self):
        from app_automation_testing.test_xueqiu_po.page.main_page import MainPage
        return MainPage(self.driver)

page/main_page.py

from appium.webdriver.common.appiumby import AppiumBy

from app_automation_testing.test_xueqiu_po.base.xueqiu_app import XueQiuApp


class MainPage(XueQiuApp):
    _search_box_element = (AppiumBy.ID, "com.xueqiu.android:id/tv_search")

    def click_search(self):
        self.find_and_click(*self._search_box_element)

        from app_automation_testing.test_xueqiu_po.page.search_page import SearchPage
        return SearchPage(self.driver)

page/search_page.py

from appium.webdriver.common.appiumby import AppiumBy
from appium.webdriver.webdriver import WebDriver

from app_automation_testing.test_xueqiu_po.base.xueqiu_app import XueQiuApp


class SearchPage(XueQiuApp):

    def input_searchcentent(self, searchkey):
        self.find_and_send(AppiumBy.ID, "com.xueqiu.android:id/search_input_text", searchkey)

        from app_automation_testing.test_xueqiu_po.page.search_result_page import SearchResultPage
        return self

    def click_searchresult(self, text):
        self.find_and_click(AppiumBy.XPATH, f'//*[@text="{text}"]')

        from app_automation_testing.test_xueqiu_po.page.search_result_page import SearchResultPage
        return SearchResultPage(self.driver)

page/search_result_page.py

from appium.webdriver.common.appiumby import AppiumBy

from app_automation_testing.test_xueqiu_po.base.xueqiu_app import XueQiuApp


class SearchResultPage(XueQiuApp):

    def goto_stock_tab(self):
        self.find_and_click(AppiumBy.XPATH, '//*[@text="股票"]')
        return self

    def get_price(self):
        current_price = self.find_and_gettext(AppiumBy.XPATH,
                                              "//*[@text='BABA']/../../..//*[@resource-id='com.xueqiu.android:id/current_price']")
        return float(current_price)

case/test_xueqiu.py

from hamcrest import assert_that, close_to

from app_automation_testing.test_xueqiu_po.base.xueqiu_app import XueQiuApp


class TestXueQiu:
    def setup_class(self):
        self.xueqiuapp = XueQiuApp()

    def setup(self):
        self.main = self.xueqiuapp.start().goto_main()

    def teardown_class(self):
        self.xueqiuapp.stop()

    def test_search(self):
        search_key = "alibaba"
        search_result = 'BABA'

        stock_price = self.main.click_search(). \
            input_searchcentent(search_key). \
            click_searchresult(search_result). \
            goto_stock_tab(). \
            get_price()

        assert_that(stock_price, close_to(110, 110 * 0.5))

    def test_search1(self):
        search_key = "alibaba"
        search_result = 'BABA'

        stock_price = self.main.click_search(). \
            input_searchcentent(search_key). \
            click_searchresult(search_result). \
            goto_stock_tab(). \
            get_price()

        assert_that(stock_price, close_to(110, 110 * 0.5))

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用户端APP自动化测试_L2 的相关文章

随机推荐

  • B/S三层架构[转载]

    三层架构 3 tier application 通常意义上的三层架构就是将整个业务应用划分为 表现层 UI 业务逻辑层 BLL 数据访问层 DAL 区分层次的目的即为了 高内聚 低耦合 的思想 表现层 UI 通俗讲就是展现给用户的界面 即用
  • 多态的理解

    多态的概念 多态的概念 简单来讲 多态的意思就是多种形态 或者说 去完成某个行为 当不同的对象去完成时会产生出不同的状态 也可以理解为我们人去完成同一件事情 但是每一个人去完成 都会产生不一样的结果 多态构成的条件 1 必须通过父类的指针或
  • qt实现9×9数独游戏

    qt实现的数独小游戏 资源有可下载直接跑的exe enigma已经打包好 源码可私信 部分代码 include widget h include ui widget h include form h include
  • python中request请求库与BeautifulSoup解析库的用法

    python中request请求库与BeautifulSoup解析库的用法 request 安装 打开cmd窗口 检查python环境 需要python3 7版本及以上 然后输入 下载requests库 pip install reques
  • element中使用v-if指令导致表单出现自动验证的问题

    当使用了element中的表单验证的时候 需注意是否在同一个标签中使用了 v if的指令 当时使用了v if时 切换显示效果时 表单出现了自动验证 解决办法 使用v show代替 在页面元素不多的话使用v show代替即可解决表单自动验证的
  • VS2017 无法连接到Web服务器“IIS Express”终极解决方案

    VS2017 无法连接到Web服务器 IIS Express 终极解决方案 今天日了gou了 一大早打开VS2017的时候出现无法连接到Web服务器 IIS Express 的错误 然后必应了一下 再谷歌了一下找到的解决方法也都千篇一律 奈
  • 框架分析(4)-Spring

    框架分析 4 Spring 专栏介绍 Spring 核心特点 控制反转 IoC 面向切面编程 AOP 组件化 集成 简化开发 总结 优缺点 优点 高度可扩展 控制反转 IoC 面向切面编程 AOP 集成支持 轻量级 测试友好 社区活跃 缺点
  • props和state的区别

    区别 1 props是传递给组件的 类似于函数的形参 而state是在组件内部被组件自己管理的 类似于在一个函数内声明的变量 2 props是不可以被修改的 state是多变的 可被修改的 开发react组件 最常用到的两个引起组件渲染的可
  • springboot 日志配置

    logback xml与logback spring xml 配置文件的加载顺序 logback xml gt application properties gt logback spring xml 如果同时存在logback xml和l
  • C#字符串数值前加0将1转化成01

    定义两个数值字符串 string str1 1 string str2 01 在我们的主观感受里这两个在进行数值比较时都是1 应该是等价的 但进行字符比对时则不尽然 转化处理 str1 Convert ToDouble str1 ToStr
  • oracle下载页面改版后的 JDK 下载、安装及其环境配置

    oracle下载页面改版后的 JDK 下载 安装及其环境配置 时间 2020 06 10 下载链接 https www oracle com java technologies javase downloads html 变量名 JAVA
  • Onnx推理框架

    Onnx推理框架 ONNX 即 Open Neural Network Exchange 当我们使用Pytorch或者TensorFlow训练完成后 通常会将其模型转化为ONNX模型 ONNX模型一般用于中间部署阶段 然后再拿转化后的ONN
  • Java的快速排序代码

    public static void quickSort int arr int start int end if start lt end int partitionIndex partition arr start end quickS
  • ETL学习心得:探求数据仓库关键环节ETL的本质

    原文链接 http hi baidu com horsewhite blog item b167f81f6924ef0a304e15a0 html 做数据仓库系统 ETL是关键的一环 说大了 ETL是数据整合解决方案 说小了 就是倒数据的工
  • spring系统架构

    一 ioc控制反转 业务层每次都要new一个新的对象来实现对实例的创建 耦合度偏高 使用IOC的最终目的是解除多个类之间的耦合性 降低代码的耦合度 IOC核心概念 ioc容器 spring容器 负责对象的创建 初始化等一系列工作 在ioc容
  • 【读书笔记】游戏开发原理

    游戏开发原理读书笔记 Contents 游戏开发原理读书笔记 一 游戏与游戏设计 1 游戏类型与平台 1 1 类型和子类型 1 2 出品类型 1 3 平台 1 4 图形类型 1 5 交付方式 1 6 视角 2 video game剖析 2
  • Unity3D之ForceMode模式

    ForceMode是一种在物理引擎中使用的模式 用于模拟对象之间的力和运动 它常用于游戏开发 虚拟现实和机器人学等领域 ForceMode通常应用于刚体 Rigidbody 对象 通过施加力来影响物体的运动 它提供了不同的模式 可以根据需求
  • gitee使用教程--初学者【超易懂】

    将本地文件上传至git 1 进入gitee官网 注册并登录 工作台 Gitee com 2 点击右上角加号 选择 新建仓库 3 给自己的仓库添加名称与项目描述 4 创建好之后会看到自己的仓库地址 5 在本地新建一个文件夹用来存放需要上传到g
  • 常量和标识符

    常量 常量是固定值 在程序执行期间不会改变 这些固定的值 又叫做字面量 常量可以是任何的基本数据类型 可分为整型数字 浮点数字 字符 字符串和布尔值 常量就像是常规的变量 只不过常量的值在定义后不能进行修改 在 C 中 有两种简单的定义常量
  • 用户端APP自动化测试_L2

    目录 appium server 环境安装 capability 进阶用法 元素定位工具 高级定位技巧 xpath 定位 高级定位技巧 css 定位与原生定位 特殊控件 toast 识别 显式等待高级使用 高级控件交互方法 设备交互api