企业微信App自动化测试
用户端 App 自动化测试
预习准备
- 提前先预习完以下相关的知识,再开始本章节的学习。
专题课 | 阶段 | 教程地址 | 视频地址 |
---|---|---|---|
用户端 APP 自动化测试 | L1 | App 自动化测试的价值与体系 | App 自动化测试的价值与体系 21:50 |
用户端 APP 自动化测试 | L1 | Appium 的基本介绍 | Appium 的基本介绍 15:36 |
用户端 APP 自动化测试 | L1 | Appium 环境安装与使用 | Appium 环境安装与使用 32:06 |
用户端 APP 自动化测试 | L1 | Appium 原理解析 | Appium 原理解析 32:46 |
用户端 APP 自动化测试 | L1 | 自动化测试用例结构分析 | 自动化测试用例结构分析 23:32 |
用户端 APP 自动化测试 | L1 | Capability 配置参数解析 | Capability 配置参数解析 13:48 |
用户端 APP 自动化测试 | L1 | App 自动化控制 | App 自动化控制 10:24 |
用户端 APP 自动化测试 | L1 | 常见控件定位方法 | 常见控件定位方法 31:20 |
用户端 APP 自动化测试 | L1 | 强制等待与隐式等待 | 强制等待与隐式等待 26:46 |
用户端 APP 自动化测试 | L1 | 常见控件交互方法 | 常见控件交互方法 29:22 |
用户端 APP 自动化测试 | L1 | 滑动交互方法 | 滑动交互方法 25:00 |
用户端 APP 自动化测试 | L1 | 自动化测试定位策略 | 自动化测试定位策略 10:13 |
用户端 APP 自动化测试 | L2 | capability 进阶用法 | capability 进阶用法 15:47 |
用户端 APP 自动化测试 | L2 | 高级定位技巧 - xpath 定位 | 高级定位技巧 - xpath 定位 31:47 |
用户端 APP 自动化测试 | L2 | 特殊控件 toast 识别 | 特殊控件 toast 识别 17:09 |
用户端 APP 自动化测试 | L2 | 显式等待高级使用 | 显式等待高级使用 21:03 |
课程目标
- 了解 Appium 原理
- 熟悉 Appium 框架与常用操作
- 掌握 App 自动化测试用例编写能力
- 掌握 App 自动化测试实战能力
知识点总览
点击查看:App 自动化测试知识点梳理.xmind
Appium 原理解析
点击查看:Appium 原理解析
需求说明
被测对象
- 企业微信
- 腾讯微信团队打造的企业通讯与办公工具,具有与微信一致的沟通体验,丰富的 OA 应用,和连接微信生态的能力。
- 可帮助企业连接内部、连接生态伙伴、连接消费者。专业协作、安全管理、人即服务。
- 前提条件:
- 手机端安装好企业微信 App。
- 企业微信注册用户。
测试需求
- 企业微信 App 搜索联系人功能自动化。
- 企业微信 App 添加成员功能自动化测试。
实战思路
实战1:环境安装与配置
课堂练习
- 完成 Appium 环境安装配置
实战2:编写搜索联系人自动化测试用例
启动 Appium 服务
使用 Appium 编写自动化测试脚本之前,需要先启动 Appium 服务,并且启动配置元素定位的工具。
梳理测试用例
测试模块 | 用例标题 | 前置条件 | 用例步骤 | 预期结果 | 实际结果 |
---|---|---|---|---|---|
成员模块 | 查询成员 | 登录成功 | 1. 进入通讯录页面 2. 点击搜索按钮,输入一个已经存在的成员名称。 |
1. 搜索结果列表页面展示该成员信息,包含其成员名称,以及公司名称。 |
- 在实现过程中,需要将代码根据测试步骤实现出来。
- 如果碰到某些控件不好定位,则需要灵活运用 xpath 的语法表达式对控件进行定位:高级定位技巧-xpath 定位
以下为搜索联系人的用例步骤:
编写测试脚本
python 代码实现:
# test_wework_contact.py
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from appium.options.common import AppiumOptions
class TestWeworkContact:
def setup_method(self):
# Capability 设置定义为字典
caps = {}
# 设置 app 安装的平台(Android、iOS)
caps["platformName"] = "Android"
# 设置 appium 驱动
caps["appium:automationName"] = "uiautomator2"
# 设备的名字
caps["appium:deviceName"] = "emulator-5554"
# 设置 app 的包名
caps["appium:appPackage"] = "com.tencent.wework"
# 设置 app 启动页
caps["appium:appActivity"] = ".launch.LaunchSplashActivity"
# 不清空缓存
caps["appium:noReset"] = True
# 强制app重启
caps["appium:forceAppLaunch"] = True
# 定义 appium 配置项
options = AppiumOptions().load_capabilities(caps)
# 初始化 driver
self.driver = webdriver.Remote(
"http://127.0.0.1:4723",
options=options
)
# 设置全局的隐式等待
self.driver.implicitly_wait(10)
def teardown_method(self):
# 关闭 driver
self.driver.quit()
def test_search_contact(self):
'''
冒烟用例,搜索存在的联系人
'''
search_key = "feier"
# 点击通讯录按钮
self.driver.find_element(
AppiumBy.XPATH,
"//*[@text='通讯录']").click()
# 点击搜索按钮
self.driver.find_element(
# ID 定位,id在不同手机上可能会变化,所以不推荐使用
# AppiumBy.ID, com.tencent.wework:id/lyp"
AppiumBy.XPATH,
# xpath 轴定位
'//*[@text="Hogwarts"]/../../../following-sibling::*/*[1]').click()
# xpath 父子定位
# '//*[@text="Hogwarts"]/../../../../*[3]/*[1]').click()
# 查找搜索框,输入搜索关键词
self.driver.find_element(
AppiumBy.XPATH,
"//*[@text='搜索']").send_keys(search_key)
# 获取搜索结果元素的文本,完成断言
eles = self.driver.find_elements(
AppiumBy.XPATH,
"//*[@class='androidx.recyclerview.widget.RecyclerView']//*[@class='android.view.ViewGroup']/*/*")
results = [ele.text for ele in eles]
print(results)
assert search_key in results
实战3:编写添加联系人自动化测试用例
梳理测试用例
测试模块 | 用例标题 | 前置条件 | 用例步骤 | 预期结果 | 实际结果 |
---|---|---|---|---|---|
成员模块 | 添加成员-成功 | 登录成功 | 1. 进入通讯录页面 2.点击添加成员,手动输入添加 3.输入正确的成员信息,点击保存 |
1. 成功添加学员,并给出“添加成功”的提示信息。 2. 添加成功的成员展示在成员列表里面 |
|
成员模块 | 添加成员-手机号重复,添加失败 | 登录成功 | 1. 进入通讯录页面 2.点击添加成员,手动输入添加 3.输入已经存在的手机号,点击保存 |
1. 提示“手机已存在于通讯录,无法添加” 2. 添加成员失败。 |
- 如果需要获取 toast 的文本信息做验证,可以参考:特殊控件 toast 识别
- 如果需要将代码滑动处理,则需要封装一个滑动操作的的方法。
编写测试脚本
# test_wework_contact.py
class TestWeworkContact:
def setup_class(self):
faker = Faker("zh_CN")
self.name = faker.name()
self.phonenum = faker.phone_number()
def setup_method(self):
# Capability 设置定义为字典
caps = {}
# 设置 app 安装的平台(Android、iOS)
caps["platformName"] = "Android"
# 设置 appium 驱动
caps["appium:automationName"] = "uiautomator2"
# 设备的名字
caps["appium:deviceName"] = "emulator-5554"
# 设置 app 的包名
caps["appium:appPackage"] = "com.tencent.wework"
# 设置 app 启动页
caps["appium:appActivity"] = ".launch.LaunchSplashActivity"
# 不清空缓存
caps["appium:noReset"] = True
# 强制app重启
caps["appium:forceAppLaunch"] = True
# 设置加载 Capability
options = AppiumOptions().load_capabilities(caps)
# 初始化 driver
self.driver = webdriver.Remote(
"http://127.0.0.1:4723",
options=options
)
# 设置全局的隐式等待
self.driver.implicitly_wait(10)
def teardown_method(self):
# 关闭 driver
self.driver.quit()
def test_add_contact(self):
'''
添加联系人
1。 点击通讯录,进入通讯录页面
2。 点击添加成员按钮,进入添加成员页面
3。 点击手动输入添加按钮,输入成员信息页面
5。 输入姓名,手机号,点击保存按钮
6。 返回添加成员页面
'''
# 点击通讯录按钮
self.driver.find_element(
AppiumBy.XPATH,
"//*[@text='通讯录']").click()
# 点击添加成员按钮
# self.driver.find_element(
# AppiumBy.XPATH,
# "//*[@text='添加成员']").click()
# 滑动点击添加成员按钮
self.swipe_find("添加成员").click()
# 点击手动输入添加按钮
self.driver.find_element(
AppiumBy.XPATH,
"//*[@text='手动输入添加']").click()
# 定位姓名输入框
self.driver.find_element(
AppiumBy.XPATH,
"//*[@text='姓名']/../*[@text='必填']"
).send_keys(self.name)
# 定位手机号输入框
self.driver.find_element(
AppiumBy.XPATH,
"//*[@text='手机']/..//*[@text='选填']"
).send_keys(self.phonenum)
# 点击保存按钮
self.driver.find_element(
AppiumBy.XPATH,
"//*[@text='保存']").click()
tips = self.driver.find_element(
AppiumBy.XPATH,
"//*[@class='android.widget.Toast']").text
assert tips == "添加成功"
def swipe_window(self):
'''
滑动界面
'''
# 滑动操作
# 获取设备的尺寸
size = self.driver.get_window_size()
# {"width": xx, "height": xx}
print(f"设备尺寸为 {size}")
width = size.get("width")
height = size.get('height')
# # 获取滑动操作的坐标值
start_x = width / 2
start_y = height * 0.8
end_x = start_x
end_y = height * 0.2
# swipe(起始x坐标,起始y坐标,结束x坐标,结束y坐标,滑动时间(单位毫秒))
self.driver.swipe(start_x, start_y, end_x, end_y, 2000)
def swipe_find(self, text, max_num=5):
'''
滑动查找
通过文本来查找元素,如果没有找到元素,就滑动,
如果找到了,就返回元素
'''
# 为了滑动操作更快速,不用等待隐式等待设置的时间
self.driver.implicitly_wait(1)
for num in range(max_num):
try:
# 正常通过文本查找元素
ele = self.driver.find_element(
AppiumBy.XPATH,
f"//*[@text='{text}']"
)
print("找到元素")
# 能找到则把隐式等待恢复原来的时间
self.driver.implicitly_wait(15)
# 返回找到的元素对象
return ele
except Exception:
# 当查找元素发生异常时
print(f"没有找到元素,开始滑动")
print(f"滑动第{num + 1}次")
# 滑动操作
self.swipe_window()
# 把隐式等待恢复原来的时间
self.driver.implicitly_wait(15)
# 抛出找不到元素的异常
raise NoSuchElementException(f"滑动之后,未找到 {text} 元素")
实战 4:用例优化
- 用例执行过程中添加截图。
- 将截图保存到 allure 报告中。
Python 实现
import os
import time
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from appium.options.common import AppiumOptions
@allure.feature("企业微信联系人操作")
class TestWithShot:
def setup_method(self):
# Capability 设置定义为字典
caps = {}
# 设置 app 安装的平台(Android、iOS)
caps["platformName"] = "Android"
# 设置 appium 驱动
caps["appium:automationName"] = "uiautomator2"
# 设备的名字
caps["appium:deviceName"] = "emulator-5554"
# 设置 app 的包名
caps["appium:appPackage"] = "com.tencent.wework"
# 设置 app 启动页
caps["appium:appActivity"] = ".launch.LaunchSplashActivity"
# 不清空缓存
caps["appium:noReset"] = True
# app 不重启
# caps["appium:dontStopAppOnReset"] = True
# 强制app重启
caps["appium:forceAppLaunch"] = True
options = AppiumOptions().load_capabilities(caps)
# 初始化 driver
self.driver = webdriver.Remote(
"http://127.0.0.1:4723",
options=options
)
# 设置全局的隐式等待
self.driver.implicitly_wait(10)
def teardown_method(self):
# 关闭 driver
self.driver.quit()
@allure.story("搜索联系人")
@allure.title("搜索联系人冒烟用例")
def test_search_contact(self):
'''
冒烟用例,搜索存在的联系人
'''
search_key = "feier"
# 点击通讯录按钮
with allure.step("点击通讯录按钮"):
image_path = self.screenshot()
self.driver.find_element(
AppiumBy.XPATH,
"//*[@text='通讯录']").click()
allure.attach.file(
image_path,
name="点击通讯录按钮",
attachment_type=allure.attachment_type.PNG
)
# 点击搜索按钮
with allure.step("点击搜索按钮"):
image_path = self.screenshot()
self.driver.find_element(
AppiumBy.XPATH,
'//*[@text="Hogwarts"]/../../../following-sibling::*/*[1]'
).click()
allure.attach.file(
image_path,
name="点击搜索按钮",
attachment_type=allure.attachment_type.PNG
)
# 查找搜索框,输入搜索关键词
with allure.step("输入搜索关键词"):
self.driver.find_element(
AppiumBy.XPATH,
"//*[@text='搜索']"
).send_keys(search_key)
image_path = self.screenshot()
allure.attach.file(
image_path,
name="输入搜索关键词",
attachment_type=allure.attachment_type.PNG
)
# 获取搜索结果元素的文本,完成断言
with allure.step("获取搜索结果元素的文本,完成断言"):
eles = self.driver.find_elements(
AppiumBy.XPATH,
"//*[@class='androidx.recyclerview.widget.RecyclerView']//*[@class='android.view.ViewGroup']/*/*")
results = [ele.text for ele in eles]
print(results)
image_path = self.screenshot()
allure.attach.file(
image_path,
name="获取搜索结果元素的文本,完成断言",
attachment_type=allure.attachment_type.PNG
)
assert search_key in results
def screenshot(self):
'''
截图
:param path: 截图保存路径
'''
# 获取当前文件所在目录的绝对路径
root_path = os.path.dirname(os.path.abspath(__file__))
# 获取当前时间
cur_time = time.strftime("%Y-%m-%d-%H-%M-%S")
# 拼接截图名称
file_path = cur_time + ".png"
# 拼接截图存放的目录
dir_path = os.sep.join([root_path, "screenshot"])
# 目录如果不存在则新创建一个
if not os.path.isdir(dir_path):
os.mkdir(dir_path)
# 拼接截图保存路径
source_path = os.sep.join([dir_path, file_path])
# 截图并保存
self.driver.save_screenshot(source_path)
# 返回保存图片的路径
return source_path
课堂练习
- 完成课堂实战练习
- 企业微信添加成员实战
总结
- Appium 环境搭建
- App 自动化测试用例编写