Skip to content

企业微信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 应用,和连接微信生态的能力。
    • 可帮助企业连接内部、连接生态伙伴、连接消费者。专业协作、安全管理、人即服务。
  • 前提条件:
    1. 手机端安装好企业微信 App。
    2. 企业微信注册用户。

测试需求

  • 企业微信 App 搜索联系人功能自动化。
  • 企业微信 App 添加成员功能自动化测试。

实战思路

uml diagram

实战1:环境安装与配置

Appium 环境安装

课堂练习
  • 完成 Appium 环境安装配置

实战2:编写搜索联系人自动化测试用例

启动 Appium 服务

使用 Appium 编写自动化测试脚本之前,需要先启动 Appium 服务,并且启动配置元素定位的工具。

Appium 配置启动

梳理测试用例
测试模块 用例标题 前置条件 用例步骤 预期结果 实际结果
成员模块 查询成员 登录成功 1. 进入通讯录页面
2. 点击搜索按钮,输入一个已经存在的成员名称。
1. 搜索结果列表页面展示该成员信息,包含其成员名称,以及公司名称。
  1. 在实现过程中,需要将代码根据测试步骤实现出来。
  2. 如果碰到某些控件不好定位,则需要灵活运用 xpath 的语法表达式对控件进行定位:高级定位技巧-xpath 定位

以下为搜索联系人的用例步骤:

uml diagram

编写测试脚本

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. 添加成员失败。
  1. 如果需要获取 toast 的文本信息做验证,可以参考:特殊控件 toast 识别
  2. 如果需要将代码滑动处理,则需要封装一个滑动操作的的方法。

uml diagram

编写测试脚本
# 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:用例优化

  1. 用例执行过程中添加截图。
  2. 将截图保存到 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 自动化测试用例编写