移动端自动化

Appium

一、移动自动化测试环境搭建

安装JDK-安装Android SDK-安装Appium-安装模拟器-安装Appium python

1、安装JDK

2、安装Android SDK

方式一:安装android studio,安装sdk

方式二:直接下载android sdk

1、下载安装包,并解压,尽量不要放置到中文目录;
2、解压之后的安装包只包含基本的SDK工具,它不包含Android平台或任何第三方库。需要使用SDKManager安装所需要的工具;
3、配置环境变量:
ANDROID_HOME=D:\Android\sdk
在Path中添加:%ANDROID_HOME%\tools;%ANDROID_HOME%\platform-tools;

提示:tools有查看元素工具,我们必须使用;platform-tools是adb命令工具所在目录。

验证安装:

C:\Users\Administrator>adb --version
Android Debug Bridge version 1.0.41
Version 33.0.3-8952118
Installed as E:\adb\platform-tools\adb.exe

3、appium安装

说明: 需要安装appium服务端程序和python中调用的api库

  • 服务端:
    作用:将脚本发送给手机
    安装:双击安装程序 appium-desktop-setup-1.8.0.exe ,一直到完成即可。
    https://github.com/appium/appium-desktop/releases
  • python的appium. api库
    作用:自动化测试使用api
    安装: pip install Appium-Python-Client==1.2.0

4、模拟器连接adb

mumu模拟器:
打开模拟器,进入安装目录,进入cmd

adb connect 127.0.0.1:7555

在这里插入图片描述


adb命令

获取包名和启动名

  • 包名:决定程序的唯一性(不是应用的名字);一个安卓应用的唯一标识符,操作应用需要依赖包名
  • 启动名:界面名(activity), 应用中界面标识符,允许重复。目前可以理解,一个界面名,对应着一个界面
    1、mac/linux: 
    adb shell dumpsys window | grep usedApp
    
    2、windows: 
    adb shell dumpsys window | findstr usedApp
    
    在这里插入图片描述

上传和下载命令

上传: adb push 路径\xxx.txt /sdcard
下载: adb pull /sdcard/xxx.txt 本地文件夹路径

启动时间命令

adb shell am start -W 包名/启动名

例:

adb shell am start -W com.android.settings/.Settings t31}

在这里插入图片描述

查看日志

adb  logcat -c && adb logcat -v time>e://1752.log

UIAutomatorViewer查看元素信息

使用JDK1.8,其他版本jdk可能会报错

查看APP元素信息步骤

1. 进入SDK目录下的目录:
mac 在 tools/bin 目录下,打开 uiautomatorviewer
windows 在 tools 目录下,打uiautomatorviewer.bat

2. 电脑连接真机或打开android模拟器
3. 启动待测试app
4. 点击 uiautomatorviewer 的左上角 Device Screenshot (从左数第二个按钮)
5. 点击希望查看的控件
6. 查看右下角 Node Detail 相关信息

在这里插入图片描述

报错问题

  • adb: adb server version (41) doesn't match this client (39);
    /bin/adb文件和~/AndroidSdk/platform-tools/adb两个adb文件,删除前者adb文件并将后者adb文件复制到/bin/目录下即可

  • Error while obtaining UI hierarchy XML file: com.android.ddmlib.SyncException: Remote object doesn't exist!
    这表示你的android设备的系统是android 9.0以上版本(包括9.0),对于9.0以上版本的android系统,uiautomatorviewer工具无法直接获取页面内容
    方式一:切换安卓版本,最有效的办法
    方式二:
    此时可以通过adb命令把页面截图和页面元素属性保存下来,然后导入uiautomatorviewer中

    • 把android设备切换到想要的画面
    • 截取uix资源文件

cmd下输入

adb shell uiautomator dump /sdcard/screen.uix //获取uix保存在android系统的/sdcard目录下 
adb pull /sdcard/screen.uix E:\screen //把android系统中的uix文件pull到电脑中

截取图片

cmd下输入

adb shell screencap -p /sdcard/screen.png //获取图片保存在android系统的/sdcard目录下 
adb pull /sdcard/screen.png E:\screen //把android系统中的图片pull到电脑中

把uix文件和png图片导入uiautomatorviewer中

二、移动自动化appium

1、appium-client执行

在这里插入图片描述
在这里插入图片描述

配置Desired Capabilities
在这里插入图片描述

配置完以后,点击Start Session

2、python脚本中,实现appium-client的功能

  • 打开模拟器
  • 启动Appium服务器
  • 获取要测试APP的包名/界面名

一定注意版本:pip install Appium-Python-Client==1.2.0
不然新版的api不相同

获取driver

from appium import webdriver

# 定义字典变量
desired_caps = {}
# 字典追加启动参数
# 需要连接的手机的平台(不限制大小写)
desired_caps["platformName"] = "Android"
# 注意:版本号必须正确
# 需要连接的手机的版本号(比如 5.2.1 的版本可以填写 5.2.1 或 5.2 或 5 ,以此类推)
desired_caps["platformVersion"] = "12"
# 需要连接的手机的设备号(andoird平台下,可以随便写,但是不能不写)
desired_caps["deviceName"] = "111"
# 需要启动的程序的包名
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)

3、基础API

启动应用

driver.start_activity(appPackage, appActivity)
appPackage:要打开的程序的包名
appActivity:要打开的程序的界面名

例:三秒后跳转通讯录

# 获取driver
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)

time.sleep(3)

# 三秒后跳转通讯录
driver.start_activity('com.android.contacts', '.activities.PeopleActivity')

driver.quit()

获取当前包名、启动名

当前应⽤包名:driver.current_package
当前应⽤启动名:driver.current_activity

# 获取driver
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)

time.sleep(3)

# 三秒后跳转通讯录
driver.start_activity('com.android.contacts', '.activities.PeopleActivity')

# 打印包名和启动名
print("当前所在应⽤包名:", driver.current_package)
print("当前所在应⽤启动名:", driver.current_activity)

driver.quit()

关闭驱动和app

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

# 关闭驱动
driver.quit()

- 关闭app后还能继续操作手机其它应用;
- 关闭驱动,可以理解为锁屏,不能在进行任何操作。

安装、卸载、是否安装 app

# 安装app,app_path为安装文件完整路径名
driver.install_app(app_path)

# 卸载app,app_id为app包名
driver.remove_app(app_id)

# 判断app是否安装,app_id为app包名
driver.is_app_installed(app_id)

置于后台

# 置于后台,seconds表示秒数
driver.background_app(seconds)

小结

跳转应用:driver.start_activity(appPackage, appActivity)
获取包名:driver.current_package
获取界面名:driver.current_activity
关闭APP:driver.close_app()
关闭驱动:driver.quit()
安装app:driver.install_app(app_path)
卸载app:driver.remove_app(app_id)
判断app是否安装:driver.is_app_installed(app_id)
置于后台:background_app(seconds)

例:

"""
     需求:
     1、判断计算器是否安装,如果安装,则进⾏卸载,否则安装
     com.google.android.calculator/com.android.calculator2.Calculator
     2、启动设置界⾯
     3、置于后台3秒钟
     4、关闭设置界⾯
     5、关闭app驱动
"""

import time
import os
from appium import webdriver

# 定义字典变量
desired_caps = {}
# 字典追加启动参数
# 需要连接的手机的平台(不限制大小写)
desired_caps["platformName"] = "Android"
# 注意:版本号必须正确
# 需要连接的手机的版本号(比如 5.2.1 的版本可以填写 5.2.1 或 5.2 或 5 ,以此类推)
desired_caps["platformVersion"] = "9"
# 需要连接的手机的设备号(andoird平台下,可以随便写,但是不能不写)
desired_caps["deviceName"] = "111"
# 需要启动的程序的包名
desired_caps["appPackage"] = "com.android.launcher3"
# 需要启动的程序的界面名
desired_caps["appActivity"] = ".Launcher"
# 设置中⽂
desired_caps["unicodeKeyboard"] = True
desired_caps["resetKeyboard"] = True

# 获取driver
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)

time.sleep(3)

is_installed = driver.is_app_installed('com.google.android.calculator')
if is_installed:
    print('计算器正在卸载...')
    # 卸载
    driver.remove_app("com.google.android.calculator")
    print('计算器卸载成功...')
else:
    print("计算器不存在,正在安装...")
    # 安装
    path = os.path.dirname(__file__)
    driver.install_app(path + "/Calculator.apk")
    print("安装计算器app成功!")

time.sleep(3)

# 启动设置界⾯
print("启动设置界⾯...")
driver.start_activity("com.android.settings", ".Settings")
time.sleep(3)
print("设置应⽤置于后台3秒钟...")
# 置于后台3秒
driver.background_app(3)

print("关闭设置app...")
# 关闭设置
driver.close_app()
print("关闭设置app后获取包名:", driver.current_package)
time.sleep(3)
print("退出driver驱动")

driver.quit()

4、元素定位API

定位单个元素

通过UiAutoMatorViewer可以获取到元素信息,appium提供根据这些信息找到具体元素方法

在这里插入图片描述

# ID定位
driver.find_element_by_id(resource-id属性值)

# class定位
driver.find_element_by_class_name(class属性值)

# xpath定位
driver.find_element_by_xpath(xpath表达式)

# name定位
driver.find_element_by_accessibility_id(content-desc属性值)

注意:
当定位到多个符合条件的元素时,默认返回第一个

例子:
需求:打开手机《设置》应用,完成下面的步骤

  • ①.使用ID定位,定位“放大镜”按钮,并点击
  • ②.使用CLASS定位,定位“输入框”,输入“hello”
  • ③.使用XPATH定位,定位“返回”,并点击
  • ④.使用NAME定位,定位“放大镜”按钮,并点击
  • ⑤.等待3s,关闭app
"""
打开手机《设置》应用,完成下面的步骤

①.使用ID定位,定位“放大镜”按钮,并点击
②.使用CLASS定位,定位“输入框”,输入“hello”
③.使用XPATH定位,定位“返回”,并点击
④.使用NAME定位,定位“放大镜”按钮,并点击
⑤.等待3s,关闭app
"""
import time

from appium import webdriver

# 定义字典变量
desired_caps = {}
# 字典追加启动参数
# 需要连接的手机的平台(不限制大小写)
desired_caps["platformName"] = "Android"
# 注意:版本号必须正确
# 需要连接的手机的版本号(比如 5.2.1 的版本可以填写 5.2.1 或 5.2 或 5 ,以此类推)
desired_caps["platformVersion"] = "9"
# 需要连接的手机的设备号(andoird平台下,可以随便写,但是不能不写)
desired_caps["deviceName"] = "111"
# 需要启动的程序的包名
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)

# ①.使用ID定位,定位“放大镜”按钮,并点击
# com.android.settings:id/search_action_bar
# ID定位
driver.find_element_by_id('com.android.settings:id/search_action_bar').click()
driver.find_element_by_xpath("//*[@class='android.widget.ImageButton']").click()

# ②.使用CLASS定位,定位“输入框”,输入“hello”
# android.view.ViewGroup
driver.find_element_by_class_name('android.view.ViewGroup').click()
driver.find_element_by_class_name('android.widget.EditText').send_keys("hello")


# ③.使用XPATH定位,定位“返回”,并点击
# "//*[@class='android.widget.ImageButton']"
driver.find_element_by_xpath("//*[@class='android.widget.ImageButton']").click()

# ④.使用NAME定位,定位“放大镜”按钮,并点击
# 向上导航
# name定位
driver.find_element_by_accessibility_id('向上导航').click()

print('流程执行完毕...')
# ⑤.等待3s,关闭app
time.sleep(3)
driver.close_app()
driver.quit()

定位一组元素

# ID定位一组
driver.find_elements_by_id(resource-id属性值)

# class定位一组
driver.find_elements_by_class_name(class属性值)

# xpath定位一组
driver.find_elements_by_xpath(xpath表达式)

# name定位一组
driver.find_elements_by_accessibility_id(content-desc属性值)

使用定位一组元素时,返回的数据是列表


需求:打开手机《设置》应用,完成下面的步骤:
①.使用ID定位,获取所有 resource-id 为 ”com.android.settings:id/title“ 的元素,并打印其文字内容
②.使用CLASS定位,获取所有class 为 ”android.widget.TextView“ 的元素,并打印其文字内容
③.使用XPATH定位,获取所有包含 ”设“ 的元素,并打印其文字内容
④.等待3s,关闭app

"""
需求:打开手机《设置》应用,完成下面的步骤:
①.使用ID定位,获取所有 resource-id 为 ”com.android.settings:id/title“ 的元素,并打印其文字内容
②.使用CLASS定位,获取所有class 为 ”android.widget.TextView“ 的元素,并打印其文字内容
③.使用XPATH定位,获取所有包含 ”设“ 的元素,并打印其文字内容
④.等待3s,关闭app
"""
import time

from appium import webdriver

# 定义字典变量
desired_caps = {}
# 字典追加启动参数
# 需要连接的手机的平台(不限制大小写)
desired_caps["platformName"] = "Android"
# 注意:版本号必须正确
# 需要连接的手机的版本号(比如 5.2.1 的版本可以填写 5.2.1 或 5.2 或 5 ,以此类推)
desired_caps["platformVersion"] = "9"
# 需要连接的手机的设备号(andoird平台下,可以随便写,但是不能不写)
desired_caps["deviceName"] = "111"
# 需要启动的程序的包名
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)

# ID定位一组
print('ID定位一组:', driver.find_elements_by_id('android:id/title'))

# class定位一组
print('class定位一组:', driver.find_elements_by_class_name('android.widget.TextView'))

# xpath定位一组
print('xpath定位一组:', driver.find_elements_by_xpath("//*[contains(@text,'设')]"))

print('流程执行完毕...')
# ⑤.等待3s,关闭app
time.sleep(3)
driver.close_app()
driver.quit()

模拟操作-点击、输入、清空

模拟点击
element.click()

模拟输入
element.send_keys(value)

清除文本
element.clear()
默认输入中文无效,但不会报错,需要在 ”前置代码“ 中增加两个参数:

desired_caps[unicodeKeyboard] = True
desired_caps['resetKeyboard'] = True

获取元素信息-文本、位置和大小

获取文本 element.text
获取位置 element.location
获取大小 element.size

例子:

# 获取driver
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)

# 2、点击放⼤镜
driver.find_element_by_id('com.android.settings:id/search_action_bar').click()

# 3、获取搜索⽂本值
# android:id/search_src_text
text = driver.find_element_by_id('android:id/search_src_text').text
print(text)

# 4、计算搜索框中⼼触摸点
size = driver.find_element_by_xpath("//*[@resource-id='android:id/search_src_text']").size
print("⼤⼩为:", size)

location = driver.find_element_by_xpath("//*[@resource-id='android:id/search_src_text']").location
print("位置:", location)

x = location.get("x") + (size.get("width") / 2)
y = location.get("y") + (size.get("height") / 2)
print("触摸中⼼位置x:{} , y:{}".format(x, y))

# 等待3s,关闭app
time.sleep(3)
driver.close_app()
driver.quit()

获取元素属性

获取元素属性
element.get_attribute(属性名)
获取属性值对应的属性名和实际UIAutoMatorViewer显示的不一定一致
获取resource-id 		resourceId
获取content-desc 	name
获取class 			className
获取text 			text

例:

# 放⼤镜
els = driver.find_elements_by_id("com.android.settings:id/search_action_bar")
for el in els:
    print("--" * 50)
    print("1、enabled属性值为:", el.get_attribute("enabled"))
    print("2、text属性值为:", el.get_attribute("text"))
    print("3、content-desc属性值为:", el.get_attribute("name"))
    print("4、resource-id属性值为:", el.get_attribute("resourceId"))
    print("5、class属性值为:", el.get_attribute("className"))
    print("--" * 50)

滑动

swipe(start_x,start_y,end_x,end_y)

  • 特点:精准滑动(基于两个坐标 点滑动)
  • 说明:针对坐标点进⾏操作
  • swipe适合多次滑动,duration时间越长惯性越大
driver.swipe(start_x, start_y, end_x, end_y, duration=None)
driver.swipe(100,1000,100,400,duration=2000)

start_x: 起点X轴坐标
start_y: 起点Y轴坐标
end_x: 终点X轴坐标
end_y: 终点Y轴坐标
duration: 滑动这个操作一共持续的时间长度,单位:ms
# 获取driver
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)
driver.implicitly_wait(10)

"""
    需求:从屏幕100,400 位置滑动到100,800位置
"""
# 练习1
driver.swipe(100, 400, 100, 800, duration=2000)

scroll滑动

  • 从一个元素滑动到另一个元素,直到页面自动停止
  • 特点:滚动(有惯性存在,滚动下不按下第⼀个元素)
  • 说明:针对两个元素进⾏操作
  • scroll滑动是两个元素之间的滑动只适合滑动一次的操作
  • 惯性很大
driver.scroll(origin_el, destination_el)

origin_el:滑动开始的元素
destination_el: 滑动结束的元素

拖拽:drag_and_drop【推荐】

  • 特点:拖拽(没有惯性,按下开始元素拖拽到指定元素位置)
  • 说明:针对两个元素进⾏精准操作
  • 从一个元素滑动到另一个元素,第二个元素替代第一个元素原本屏幕上的位置
  • scroll滑动是两个元素之间的滑动只适合滑动一次的操作
  • 无惯性
driver.drag_and_drop(origin_el, destination_el)

origin_el:滑动开始的元素
destination_el: 滑动结束的元素
# 获取driver
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)
sleep(1)
driver.implicitly_wait(10)

"""
    需求:
        1、从应用滑动到更多 
        2、从应用拖拽到更多
"""
exe = driver.find_element_by_xpath("//*[@text='应用']")
more = driver.find_element_by_xpath("//*[@text='更多']")

# 1、滚动
# driver.scroll(exe,more)
# 2、拖拽
driver.drag_and_drop(exe,more)

5、高级手势Api

高级手势TouchAction:轻敲、按下、抬起、等待、长按、移动

实现步骤:

# 创建TouchAction对象
touch_action = TouchAction(driver)

# 调用高级手势对象提供所想执行的手势方法
touch_action.手势方法

# 执行手势
touch_action.perform()

所有手势都要通过执行perform()函数才会运行
也可以链式调用

TouchAction(driver).手势方法.perform()

轻敲

轻敲:模拟手指对某个元素或坐标按下并快速抬起

- 创建TouchAction对象
touch_action = TouchAction(driver)

- 调用轻敲,元素对象或坐标二选一
touch_action.tap(element=None,x=None,y=None)

- 执行手势
touch_action.perform()

例子:

# 获取driver
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)
driver.implicitly_wait(10)

# - 调用轻敲,元素对象或坐标二选一
for i in range(5):
    TouchAction(driver).tap(element=None, x=768, y=697).perform()

time.sleep(5)
driver.quit()

按下和抬起

按下和抬起 模拟手指一直按下,模拟手指抬起。可以用来组合成轻敲或长按的操作

实现方法
- 创建TouchAction对象
touch_action = TouchAction(driver)
- 调用按下,元素对象或坐标二选一
touch_action.press(el=None,x=None,y=None)
- 调用抬起
touch_action.release()
- 执行手势
touch_action.perform()

- 换个写法,连续调用也OK。
TouchAction(driver).press(el=None,x=None,y=None).perform()
TouchAction(driver).release().perform()

例子:

# 获取driver
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)
driver.implicitly_wait(10)

TouchAction(driver).press(el=None, x=768, y=697).perform()
TouchAction(driver).release().perform()

time.sleep(5)
driver.quit()


"""
    手势操作:TaouchAction类
        1、轻敲:tap(元素,x,y)
        2、按下:press
        3、抬起:release()
    重点:执行手势,必须调用perform()方法
    # 需求:
        1、使用按下和抬起 点击WLAN 
"""
# 获取WLAN元素
wlan = driver.find_element_by_xpath("//*[@text='WLAN']")
loc = wlan.location
print(loc)

# 效果类似点击
TouchAction(driver).press(x=loc.get("x"),y=loc.get("y")).release().perform()

长按

长按 模拟手指对元素或坐标的长按操作

实现方法
- 模拟手指对元素或者坐标的长按操作。
TouchAction(driver).long_press(el=None,duration=1000).perform()
TouchAction(driver).long_press(x=None,y=None,duration=1000).perform()

例子

"""
    手势操作:TaouchAction类
        1、轻敲:tap(元素,x,y)
        2、按下:press
        3、抬起:release()
        4、长安:long_press()
    重点:执行手势,必须调用perform()方法
    # 需求:
        1、使用点击WLAN 
        2、长按3秒无线,出现菜单(修改网络)
"""
# 获取WLAN元素
wlan = driver.find_element_by_xpath("//*[@text='WLAN']")
wlan.click()
# 必须暂时一定时间 暂停3秒
sleep(3)
# 效果类似点击
TouchAction(driver).long_press(x=777,y=375).perform()

移动和思考时间(移动和等待)

手指移动 :模拟手指移动操作
思考时间 :模拟手指等待,在执行下一步时等待一段指定时间

实现方法
- 模拟手指对元素或坐标的移动操作。
TouchAction(driver).move_to(el=None,x=None,y=None).perform()

- 模拟手指暂停到当前动作指定时间。
TouchAction(driver).wait(ms=time).perform()

例子:


"""
    需求:绘制解锁图案 Z
"""
# 应用和通知 滑动到 存储
exe = driver.find_element_by_xpath("//*[@text='应用和通知']")
more = driver.find_element_by_xpath("//*[@text='存储']")
driver.drag_and_drop(more, exe)

loc = driver.find_element_by_xpath("//*[@text='安全性和位置信息']")
TouchAction(driver).tap(element=loc).perform()

# 打开设置屏幕锁定方式 点击绘图
TouchAction(driver).tap(element=None, x=150, y=360).perform()
# 点击图案
TouchAction(driver).tap(element=driver.find_element_by_xpath("//*[@text='图案']")).perform()
# 绘制Z
TouchAction(driver).press(x=681, y=463).wait(100).move_to(x=922, y=456).wait(100).move_to(x=680, y=696).wait(
    100).move_to(x=919, y=696).wait(100).perform()

6、手机操作

获取分辨率和截图

实现方法

获取手机分辨率。
driver.get_window_size()

获取手机截图。
get_screenshot_as_file(filename)

获取当前网络类型、设置手机网络网络类型

实现方法

- 获取手机网络。
driver.network_connection()

- 获取手机网络。
driver.set_network_connection(connectionType)
connectionType:表示网络设置类型

网络类型:
在这里插入图片描述
例子:

"""
    需求:
        1、查看当前网络类型
        2、设置网络类型为飞行模式
        3、获取当前屏幕分辨率
        4、截图保存
"""
# 1、查看当前网络类型
print("当前网络类型为:",driver.network_connection)

# 2、设置网络类型为飞行模式
driver.set_network_connection(6)
print("设置之后的网络类型为:",driver.network_connection)

# 3、获取当前屏幕分辨率
print("当前屏幕分辨率为:",driver.get_window_size())
# 4、截图保存
driver.get_screenshot_as_file("./screen.png")

按键

发送键到设备。
driver.press_keycode(keycode)
keycode:表示手机设备的默认键码

Android KeyCode列表参考

  1. 常用字母数字键
/**
 * 0- 9(数字键盘)
 */
public static final int KEYCODE_0             = 7;
public static final int KEYCODE_1             = 8;
public static final int KEYCODE_2             = 9;
public static final int KEYCODE_3             = 10;
public static final int KEYCODE_4             = 11;
public static final int KEYCODE_5             = 12;
public static final int KEYCODE_6             = 13;
public static final int KEYCODE_7             = 14;
public static final int KEYCODE_8             = 15;
public static final int KEYCODE_9             = 16;

/**
 * A键 - Z键 (字母键盘)
 */
public static final int KEYCODE_A             = 29;
public static final int KEYCODE_B             = 30;
public static final int KEYCODE_C             = 31;
public static final int KEYCODE_D             = 32;
public static final int KEYCODE_E             = 33;
public static final int KEYCODE_F             = 34;
public static final int KEYCODE_G             = 35;
public static final int KEYCODE_H             = 36;
public static final int KEYCODE_I             = 37;
public static final int KEYCODE_J             = 38;
public static final int KEYCODE_K             = 39;
public static final int KEYCODE_L             = 40;
public static final int KEYCODE_M             = 41;
public static final int KEYCODE_N             = 42;
public static final int KEYCODE_O             = 43;
public static final int KEYCODE_P             = 44;
public static final int KEYCODE_Q             = 45;
public static final int KEYCODE_R             = 46;
public static final int KEYCODE_S             = 47;
public static final int KEYCODE_T             = 48;
public static final int KEYCODE_U             = 49;
public static final int KEYCODE_V             = 50;
public static final int KEYCODE_W             = 51;
public static final int KEYCODE_X             = 52;
public static final int KEYCODE_Y             = 53;
public static final int KEYCODE_Z             = 54;
  1. 功能键
    功能键位于键盘的左下角,主要控制输入的操作,比如回车、删除等。
public static final int KEYCODE_ENTER           = 66; //回车
public static final int KEYCODE_DEL             = 67; //删除
public static final int KEYCODE_TAB             = 61; //Tab

/**
 * 方向键
 */
public static final int KEYCODE_DPAD_UP         = 19; // 上箭头
public static final int KEYCODE_DPAD_DOWN       = 20; // 下箭头
public static final int KEYCODE_DPAD_LEFT       = 21; // 左箭头
public static final int KEYCODE_DPAD_RIGHT      = 22; // 右箭头
  1. 控制键
    如Home按钮、Menu按钮、返回按钮、音量键、亮度键等控制设备的按键。
/**
 * 控制键
 */
public static final int KEYCODE_BACK            = 4;  //返回键(左下角)
public static final int KEYCODE_HOME            = 3;  //Home键(中间大圆钮)
public static final int KEYCODE_APP_SWITCH      = 187; //任务键(最近打开的应用列表键) 
public static final int KEYCODE_VOLUME_UP       = 24; //音量增加
public static final int KEYCODE_VOLUME_DOWN     = 25; //音量减少
public static final int KEYCODE_BRIGHTNESS_UP   = 220;//增加亮度
public static final int KEYCODE_BRIGHTNESS_DOWN = 221;//降低亮度

例子:

"""
    需求:
       1、点击三次音量+  24
       2、点击返回       4
       3、点击两次音量-  25
"""
i = 0
# 三次增大音量
while i < 3:
    driver.press_keycode(24)
    print('voice +')
    i += 1

# 点击返回
driver.press_keycode(4)
print('return')
i = 0
# 两次减小音量
while i < 2:
    driver.press_keycode(25)
    print('voice -')
    i += 1

操作通知栏

应⽤场景:检查服务器发送的通知

- 打开通知栏。
driver.open_notifications()

appium官方并没有为我们提供关闭通知的api,那么现实生活中怎么关闭,就怎样操作就行,比如,手指从下往上滑动,或者,按返回键

例子

"""
 需求:
 1、打开通知栏,点击通知栏的信息
"""
# 打开通知栏
driver.open_notifications()
sleep(2)
# 查找信息并点击
driver.find_element_by_xpath("//*[@text='应⽤宝.apk']").click()

7、其他

- toast获取

toast消息为移动应⽤中,⼀种⿊底⽩字提示信息,有时间限制。
在这里插入图片描述

  • 为什么要获取toast消息?
    断⾔内容

步骤

  • 安装依赖库
    • 安装依赖库: pip install uiautomator2
  • 配置driver启⽤参数
    desired_caps['automationName'] = 'Uiautomator2'
    
  • 编写代码获取⽂本值
    # 获取toast消息
    msg = driver.find_element_by_xpath("//*[contains(@text,'请先勾选同意')]").text
    print("toast消息为:",msg)
    

例子:

from time import sleep

from appium import webdriver
# 定义字典变量
from appium.webdriver.common.touch_action import TouchAction

desired_caps = {}
# 字典追加启动参数
desired_caps["platformName"] = "Android"
desired_caps["platformVersion"] = "6.0.1"
desired_caps["deviceName"] = "192.168.56.101:5555"
desired_caps["appPackage"] = "com.netease.newsreader.activity"
desired_caps["appActivity"] = "com.netease.nr.phone.main.MainActivity"
# 获取toast
desired_caps['automationName'] = 'Uiautomator2'
# 设置中文
desired_caps["unicodeKeyboard"] = True
desired_caps["resetKeyboard"] = True
# 获取driver/
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)
driver.implicitly_wait(10)
"""
    需求:
       1、获取网易新闻未同意协议进行登录  --> toast消息
"""

# 点击未登录
driver.find_element_by_xpath("//*[@text='未登录']").click()
# 点击登录
driver.find_element_by_xpath("//*[@text='登录']").click()
# 点击微信登录
driver.find_element_by_xpath("//*[@text='微信登录']").click()

# 获取toast消息
msg = driver.find_element_by_xpath("//*[contains(@text,'请先勾选同意')]").text
print("toast消息为:",msg)


sleep(3)
driver.quit()

- webview App测试

WebView App 通过手机浏览器访问的项目

  • 项目页面上的元素不能直接通过UiAutoMatorView查看元素
  • 不能直接进行定位
  • 打开项目需要先操作Native App(浏览器)网址的输入框
  • 需要浏览器驱动环境才能实现
  • app中嵌套web信息,如果不切换⽆法定位操作
# 获取当前所有的环境
print(driver.contexts)
# 切换环境
driver.switch_to.context("WEBVIEW_com.android.browser")

例子:

from time import sleep

from appium import webdriver
# 定义字典变量
from appium.webdriver.common.touch_action import TouchAction

desired_caps = {}
# 字典追加启动参数com.tencent.news/.activity.SplashActivity
desired_caps["platformName"] = "Android"
desired_caps["platformVersion"] = "6.0.1"
desired_caps["deviceName"] = "192.168.56.101:5555"
desired_caps["appPackage"] = "com.android.browser"
desired_caps["appActivity"] = ".BrowserActivity"
# 获取toast
desired_caps['automationName'] = 'Uiautomator2'
# 设置中文
desired_caps["unicodeKeyboard"] = True
desired_caps["resetKeyboard"] = True
# 获取driver
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)
driver.implicitly_wait(10)

# 打开浏览器
driver.find_element_by_xpath("//*[@resource-id='com.android.browser:id/url']").send_keys("https://m.baidu.com/")
# 输入回车
driver.press_keycode(66)
sleep(3)

# 获取当前所有的环境
print(driver.contexts)
# 切换环境
driver.switch_to.context("WEBVIEW_com.android.browser")
# 定位元素操作
driver.find_element_by_xpath("//*[@id='index-kw']").send_keys("test123456")

sleep(3)
driver.quit()

- Monkey

主要用于Android 的压力测试 自动的一个压力测试小工具, 主要目的就是为了测试app是否会Crash.

  • 安装
    不需要安装,Monkey程序由Android系统自带,使用Java语言写成,在Android文件系统中的存放路径是:/system/framework/monkey.jar;

  • 启动方式

    • 可以通过PC机CMD窗口中执行: adb shell monkey {+命令参数}来进行Monkey测试;
    • 在PC上adb shell 进入Android系统,通过执行 monkey {+命令参数}来进行Monkey 测试;
  • 使用monkey测试 包 随机事件100次 输入日志文件

    adb shell monkey -p cn.goapk.market 100 > 路径/log.txt
    
    adb shell monkey -p com.yunmall.lc -v -v 10000 >xxx.log
    -p :包名
    -v -v :⽇志
    10000 :乱抓的时间次数
    -s :设置随机种⼦数(两次执⾏随机种⼦数⼀样,执⾏的事件也是⼀样)
    

参数

adb shell monkey -p fishjoy.control.menu -p cn.goapk.market 100

指定app包
-p {被测试的app包名}
用此参数指定一个或多个包。
指定包之后,monkey将只允许系统启动指定的app。
如果指定包列表, monkey将允许系统启动设备中的所有app。
adb shell monkey -p cn.goapk.market -v -v 100

日志级别
-v 通过多个-v的个数来指定查看日志级别。

-v :缺省值,仅提供启动提示、测试完成和最终结果等少量信息
-v -v :提供较为详细的日志,包括每个发送到Activity的事件信息
-v -v -v :最详细的日志,包括了测试中选中/未选中的Activity信息
随机种子数
-s (随机数种子) 
用于指定伪随机数生成器的seed值,如果seed相同,则两次Monkey测试所产生的事件序列也相同

monkey测试1:adb shell monkey -p cn.goapk.market –s 10 100
monkey测试2:adb shell monkey -p cn.goapk.market –s 10 100
事件间隔时间 --throttle <毫秒>
adb shell monkey -p cn.goapk.market --throttle 3000 100

在这里插入图片描述

monkey测试执行和日志分析

  • 一般在功能测试完毕之后

  • 执行方式:

    • 多次小批量事件测试≤10W次事件
    • 大批量随机性测试
    • 正常情况
      • 如果Monkey测试顺利执行完成,在log的最后,会打印出当前执行事件的次数和所花费的时间; //Monkey finished 代表执行完成
      • 执行过程中出现无响应、崩溃或异常退出后,打开日志进行分析
    • 异常情况
        1. 程序无响应的问题: 在日志中搜索“ANR” (可能仅仅是因为卡)
        1. 崩溃问题:在日志中搜索“Exception” (如果出现空指针,NullPointerException) 肯定是有bug Monkey执行中断,在log最后也能看到当前执行次数

8、报错问题

java.lang.SecurityException: Permission Denial: starting Intent

三、appium+selenium+po封装

需求:
1、app订单业务
在这里插入图片描述
2、web发货业务
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

page页面公共方法

base.py

# page页面公共方法

import os
import time
from selenium.webdriver.support.wait import WebDriverWait

from base import log
from config import DIR_PATH


class Base:

    # app下单流程自动化用例实现
    # 初始化方法
    def __init__(self, driver):
        log.info("正在初始化,driver对象:{}".format(driver))
        self.driver = driver

    # 查找元素方法
    def base_find(self, loc, timeout=10, poll=0.5):
        # .format方法,{}占位符会替换为loc
        print("-->正在查找:{}".format(loc))
        # * 是一个解包运算符,它用于将可迭代对象(例如列表、元组)解包成单独的元素
        return WebDriverWait(self.driver, timeout, poll).until(lambda x: x.find_element(*loc))

    # 点击方法
    def base_click(self, loc):
        self.base_find(loc).click()

    # 输入方法
    def base_input(self, loc, value):
        # 获取元素
        el = self.base_find(loc)
        # 清空
        el.clear()
        # 输入
        el.send_keys(value)

    # 获取文本方法
    def base_get_text(self, loc):
        return self.base_find(loc).text

    # 截图方法
    def base_get_img(self):
        img_path = DIR_PATH + os.sep + "img" + os.sep + "{}.png".format(time.strftime("%Y%m%d%H%M%S"))
        self.driver.get_screenshot_as_file(img_path)

    # 2、web后台实现发货业务
    # 由于web发货页面需要切换iframe标签,所以在Base类中新增两个方法(1、切换frame 2、回到默认目录)
    # 切换frame
    def base_switch_frame(self, loc):
        # 获取元素
        el = self.base_find(loc)
        # 执行切换
        self.driver.switch_to.frame(el)

    # 恢复frame
    def base_default_frame(self):
        self.driver.switch_to.default_content()

页面page

1、页面模块数据

page/__init__.py

from selenium.webdriver.common.by import By

order_on = None

"""
    一、以下为app登录模块配置信息    
"""
# 我的
app_login_me = By.XPATH, "//*[@text='我的']"
# 登录图片(连接)
app_login_link = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/head_img']"
# 用户名
app_username = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/mobile_et']"
# 密码
app_pwd = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/pwd_et']"
# 协议
app_pro = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/agree_btn']"
# 登录按钮
app_login_btn = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/login_tv']"
# 昵称
app_nickname = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/nick_name_tv']"
"""
    二、以下为app订单模块配置信息
"""
# 首页
app_order_index = By.XPATH, "//*[@text='首页']"
# 搜索框 -1  android.widget.EditText
app_order_search_text1 = By.XPATH, "//*[@class='android.widget.EditText']"
# 搜索框 -2 com.tpshop.malls:id/search_et
app_order_search_text2 = By.XPATH, "//*[@class='android.widget.EditText']"
# 搜索按钮
app_order_search_btn = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/search_btn']"
# 点击第一张图片 com.tpshop.malls:id/product_pic_img
app_order_img = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/product_pic_img']"
# 加入购物车 com.tpshop.malls:id/add_cart_tv
app_order_add_cart = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/add_cart_tv']"
# 确定
app_order_cart_ok = By.XPATH, "//*[@text='确定']"
# 购物车
app_order_cart = By.XPATH, "//*[@text='购物车']"
# 立即购买
app_order_now_purchase = By.XPATH, "//*[@text='立即购买']"
# 提交订单
app_order_submit_order = By.XPATH, "//*[@text='提交订单']"
# 点击立即支付
app_order_now_pay = By.XPATH, "//*[@text='立即支付']"
# 输入支付密码 com.tpshop.malls:id/pwd_et
app_order_pay_pwd = "//*[@resource-id='com.tpshop.malls:id/pwd_et']"
# 确定
app_order_pay_ok = By.XPATH, "//*[@text='确定']"
# 订单编号 com.tpshop.malls:id/pay_trade_no_tv
app_order_no = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/pay_trade_no_tv']"

"""
三、web后台登录配置信息整理
"""
# 用户名
web_login_username = By.CSS_SELECTOR, "[name='username']"
# 密码
web_login_pwd = By.CSS_SELECTOR, "[name='password']"
# 验证码
web_login_verify = By.CSS_SELECTOR, "[name='vertify']"
# 登录按钮
web_login_submit = By.CSS_SELECTOR, "[name='submit']"
# 昵称
web_login_nickname = By.CSS_SELECTOR, ".bgdopa-t"

# 订单菜单
web_order = By.XPATH, "//a[text()='订单']"
# 左侧菜单 发货单
web_order_goods = By.XPATH, "//a[text()='发货单']"
# iframe
web_order_iframe = By.CSS_SELECTOR, "#workspace"
# 去发货 //div[text()='202112161517008312']/../..//td[@class='handle']//a[1]
web_order_go_goods = By.XPATH, "//a[text()='去发货']"
# 物流公司
web_order_company = By.CSS_SELECTOR, "[value='YZPY']"
# 配送单号
web_order_order_no = By.CSS_SELECTOR, "#invoice_no"
# 确认发货
web_order_goods_ok = By.CSS_SELECTOR, "ncap-btn-send"
# 打印配置单
web_order_print_order = By.CSS_SELECTOR, ".fa-print"
# 获取订单编号
web_order_on = By.XPATH, "//div[@id='printDiv']/div[@class='contactinfo']/dl[1]/dd[2]"
# 难点:如何根据指定单号,查找对应的去发货元素?
# 思路:想找共同的父级,在逐级查找
2、app登录page结构(page_app_login.py)
from time import sleep

import page
from base.base import Base

"""
    将操作步骤进行封装+业务组合方法
"""


class PageAppLogin(Base):

    # 1、点击 我的
    def page_app_click_me(self):
        self.base_click(page.app_login_me)

    # 2、点击 登录头像
    def page_app_click_login_link(self):
        self.base_click(page.app_login_link)

    # 3、输入用户名
    def page_app_input_username(self, username):
        self.base_input(page.app_username, username)

    # 4、输入密码
    def page_app_input_pwd(self, pwd):
        self.base_input(page.app_pwd, pwd)

    # 5、点击勾选协议
    def page_app_click_pro(self):
        self.base_click(page.app_pro)

    # 6、点击登录按钮
    def page_app_click_login_btn(self):
        self.base_click(page.app_login_btn)

    # 7、获取登录昵称
    def page_app_get_nickname(self):
        sleep(2)
        return self.base_get_text(page.app_nickname)

    # 8、组合业务方法
    def page_app_login(self, username="13600001111", pwd="123456"):
        self.page_app_click_me()
        self.page_app_click_login_link()
        self.page_app_input_username(username)
        self.page_app_input_pwd(pwd)
        self.page_app_click_pro()
        self.page_app_click_login_btn()

3、订单业务结构搭建(page_order.py)
from time import sleep
import page
from base.base import Base


class PageAppOrder(Base):
    # 点击首页
    def page_app_click_index(self):
        sleep(1)
        self.base_click(page.app_order_index)

    # 点击搜索框
    def page_app_click_search_text(self):
        sleep(1)
        self.base_click(page.app_order_search_text1)

    # 输入搜索内容
    def page_app_input_search_text(self,value="小米"):
        sleep(1)
        self.base_input(page.app_order_search_text1,value)

    # 点击搜索按钮
    def page_app_click_search_btn(self):
        sleep(1)
        self.base_click(page.app_order_search_btn)

    # 选择商品
    def page_app_select_photo(self):
        sleep(1)
        self.base_click(page.app_order_img)

    # 点击加入购物车
    def page_app_add_cart(self):
        sleep(1)
        self.base_click(page.app_order_add_cart)

    # 点击确定
    def page_app_cart_ok(self):
        sleep(1)
        self.base_click(page.app_order_cart_ok)

    # 点击购物车
    def page_app_click_cart(self):
        sleep(1)
        self.base_click(page.app_order_cart)

    # 点击立即购买
    def page_app_now_purchase(self):
        sleep(1)
        self.base_click(page.app_order_now_purchase)

    # 点击提交订单
    def page_app_click_submit_order(self):
        sleep(1)
        self.base_click(page.app_order_submit_order)

    # 点击 立即支付
    def page_app_click_now_pay(self):
        sleep(1)
        self.base_click(page.app_order_now_pay)

    # 输入 密码
    def page_app_input_pwd(self,pwd="123456"):
        sleep(1)
        self.base_input(page.app_pwd,pwd)

    # 点击确定
    def page_app_click_sure(self):
        sleep(1)
        self.base_click(page.app_order_pay_ok)

    # 获取订单编号
    def page_app_get_order_on(self):
        sleep(2)
        return self.base_get_text(page.app_order_no)

    # 下单业务方法
    def page_app_order(self,search_value="小米", pwd="123456"):
        self.page_app_click_index()
        self.page_app_click_search_text()
        self.page_app_input_search_text(search_value)
        self.page_app_click_search_btn()
        self.page_app_select_photo()
        self.page_app_add_cart()
        self.page_app_cart_ok()
        self.page_app_click_cart()
        self.page_app_now_purchase()
        self.page_app_click_submit_order()
        self.page_app_click_now_pay()
        self.page_app_input_pwd(pwd)
        self.page_app_click_sure()
4、web登录
import page
from base.base import Base


class PageWebLogin(Base):
    # 1、输入用户名
    def page_web_username(self, value):
        self.base_input(page.web_login_username, value)

    # 2、输入密码
    def page_web_pwd(self, value):
        self.base_input(page.web_login_pwd, value)

    # 3、输入验证码
    def page_web_verify_code(self, value):
        self.base_input(page.web_login_verify, value)

    # 4、点击登录按钮
    def page_web_login_btn(self):
        self.base_click(page.web_login_submit)

    # 5、获取登录昵称
    def page_web_nickname(self):
        return self.base_get_text(page.web_login_nickname)

    # 登录业务方法
    def page_web_login(self, username="admin", pwd="123456", code="8888"):
        self.page_web_username(username)
        self.page_web_pwd(pwd)
        self.page_web_verify_code(code)
        self.page_web_login_btn()

5、web订单
import page, time
from base.base import Base


class PageWebOrder(Base):
    # 订单菜单
    def page_web_order_menu(self):
        self.base_click(page.web_order)

    # 左侧 发货单
    def page_web_order_goods(self):
        self.base_click(page.web_order_goods)

    # 工作区域 去发货
    def page_web_go_goods(self):
        # 切换iframe
        self.base_switch_frame(page.web_order_iframe)
        self.base_click(page.web_order_go_goods)

    # 物流公司
    def page_order_company(self):
        self.base_click(page.web_order_company)

    # 配送单号
    def page_order_input_order_no(self):
        value = str(time.strftime("%Y%m%d%H%M%S"))
        self.base_input(page.web_order_order_no, value)

    # 确认发货
    def page_order_goods_ok(self):
        self.base_click(page.web_order_goods_ok)

    # 打印配置单
    def page_order_print_order(self):
        self.base_click(page.web_order_print_order)

    # 获取订单编号
    def page_order_get_on(self):
        return self.base_get_text(page.web_order_on)

    # 订单发货业务
    def page_order_go_goods(self):
        self.page_web_order_menu()
        self.page_web_order_goods()
        self.page_web_go_goods()
        self.page_order_company()
        self.page_order_input_order_no()
        self.page_order_goods_ok()
        self.page_order_print_order()

6、工具类

utils.py

import os
import json

import appium.webdriver
import logging.handlers

from selenium import webdriver
from config import DIR_PATH, HOST


class GetDriver:
    __app_driver = None
    __web_driver = None

    # 获取App Driver
    @classmethod
    def get_app_driver(cls):
        if cls.__app_driver is None:
            # 设置启动
            desired_caps = {}
            # 必填-且正确
            desired_caps['platformName'] = 'Android'
            # 必填-且正确
            desired_caps['platformVersion'] = '9'
            # 必填
            desired_caps['deviceName'] = '192.168.0.111:5555'
            # APP包名 com.tpshop.malls/.SPMainActivity
            desired_caps['appPackage'] = "com.tpshop.malls"
            # 启动名
            desired_caps['appActivity'] = ".SPMainActivity"
            # 设置中文
            desired_caps['unicodeKeyboard'] = True
            desired_caps['resetKeyboard'] = True
            # 设置driver
            cls.__app_driver = appium.webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)
        return cls.__app_driver

    # 获取Web Driver
    @classmethod
    def get_web_driver(cls):
        if cls.__web_driver is None:
            cls.driver = webdriver.Chrome()
            cls.driver.get(HOST)
            cls.driver.maximize_window()
            return cls.driver


# 读取json工具
def read_json(filename, key):
    file_path = DIR_PATH + os.sep + "data" + os.sep + filename
    arrays = []
    with open(file_path, "r", encoding="utf-8")as f:
        for data in json.load(f).get(key):
            arrays.append(tuple(data.values())[1:])
        return arrays


def write_json(value):
    file_path = DIR_PATH + os.sep + "data" + os.sep + "expect.json"
    with open(file_path, "w", encoding="utf-8")as f:
        data = {"expect": [{"desc": "app订单编号", "order_no": value}]}
        json.dump(data, f)


# 日志封装
# 日志应用
# 1、记录程序运行步骤 base->info
# 2、记录程序错误 script->error

class GetLog:
    __log = None

    @classmethod
    def get_log(cls):
        if cls.__log is None:
            # 获取日志器
            cls.__log = logging.getLogger()
            # 设置入口级别
            cls.__log.setLevel(logging.INFO)
            # 获取处理器
            filename = DIR_PATH + os.sep + "log" + os.sep + "tpshop_auto.log"
            tf = logging.handlers.TimedRotatingFileHandler(filename=filename,
                                                           when="midnight",
                                                           interval=1,
                                                           backupCount=3,
                                                           encoding="utf-8")

            # 获取格式器
            fmt = "%(asctime)s %(levelname)s [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s"
            fm = logging.Formatter(fmt)
            # 将格式器添加到处理器
            tf.setFormatter(fm)
            # 将处理器添加到日志器
            cls.__log.addHandler(tf)
        # 返回日志器
        return cls.__log


if __name__ == '__main__':
    GetDriver.get_app_driver()

7、config.py
import os

# 项目路径
DIR_PATH = os.path.dirname(__file__)

# web后台url
HOST = "http://hmshop-test.net/admin"

8、run_suite
# 执行自动化测试并生成报告
import os
import unittest
from htmltestreport import HTMLTestReport

from config import DIR_PATH

suite = unittest.defaultTestLoader.discover("./script")
file_path = DIR_PATH + os.sep + "report" + os.sep + "tpshop_auto.html"
HTMLTestReport(file_path).run(suite)
  • 15
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值