Android APP 自动化测试教程 (混合驱动版)

本教程介绍如何搭建基于 Python + Pytest + Allure 的测试框架。
核心策略:使用 Uiautomator2 进行高效的原生控件定位(文字、ID),使用 Airtest 处理复杂的自定义 UI 或图标识别。


1. 安装 Android 调试工具 (ADB)

  1. 下载 platform-tools.zip (见附件或 官网下载)。
  2. 解压并配置环境变量 Path 指向解压目录。
  3. 终端执行 adb --version 验证安装。

2. 安装 Python 3.9

  • 推荐版本:Python 3.8 或 3.9。
  • python安装参考:python安装教程
  • 环境管理:建议安装 pipenv
    pip install pipenv
    

3. 安装 JDK 8

  • 用于 Allure 报告生成,必须安装 JDK 1.8。
  • 安装后执行 java -version 验证。
    jdk安装教程参考:jdk8安装教程

4. 安装 PyCharm

5. 安装辅助工具

Airtest IDE

  • 用于截图录制,获取图片代码。
  • 下载地址

Weditor (推荐配合 Uiautomator2)

  • 用于查看原生控件属性 (Resource-ID, XPath)。
  • 安装命令:
    pip install weditor
    
  • 启动命令:
    python -m weditor
    

还有uiauto.dev也可以用

6. 安装 Allure

  • 下载并配置 Allure 命令行工具,确保 bin 目录在环境变量中。
  • 验证:allure --version
  • 安装参考:安装配置allure教程

7. 项目依赖安装 (必做)

在项目根目录下,使用终端安装以下库:

# 1. 创建虚拟环境 (可选,如果不使用虚拟环境直接跳过此行)
pipenv install --python 3.9

# 2. 安装自动化核心库
# uiautomator2: 原生元素定位
# airtest: 图像识别
# pytest: 测试框架
# allure-pytest: 报告插件
pip install uiautomator2 airtest pytest allure-pytest

首次连接手机前,需初始化 uiautomator2 守护程序:

python -m uiautomator2 init

8. 完整代码示例 (直接复制)

新建一个 Python 文件(例如 test_hybrid_demo.py),内容如下。该代码封装了两种查找方式,并演示了如何在一个测试类中同时使用它们。

import os
import time
import logging
import pytest
import allure
import uiautomator2 as u2
from uiautomator2._selector import UiObject
from airtest.core.api import *
from airtest.core.error import TargetNotFoundError

# ==================== 1. 全局配置 ====================
# 配置日志格式
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

DEFAULT_TIMEOUT = 10

# ==================== 2. 核心封装函数 ====================

def take_screenshot(name_suffix):
    """
    截图并附加到 Allure 报告中
    """
    try:
        # 使用 Airtest 的截图功能
        filename = f"err_{int(time.time())}_{name_suffix}.png"
        snapshot(filename=filename, msg=name_suffix)
        
        if os.path.exists(filename):
            with open(filename, "rb") as f:
                allure.attach(f.read(), name=name_suffix, attachment_type=allure.attachment_type.PNG)
            # 截图后可选择删除本地文件,保持目录整洁
            # os.remove(filename) 
    except Exception as e:
        logging.error(f"截图失败: {e}")

def find_image(
    image: Template,
    expected_found=True,
    should_log_errors=True,
    timeout=DEFAULT_TIMEOUT,
) -> bool:
    """
    【Airtest】基于图像识别查找
    """
    try:
        # 尝试等待图片出现
        wait(image, timeout=timeout)
        real_found = True
    except TargetNotFoundError:
        real_found = False
    
    # 异或逻辑:如果期望值与实际值不一致,则结果为 False
    result = not (expected_found ^ real_found)
    
    if not result and should_log_errors:
        if expected_found:
            logging.error(f"未找到图片元素({image.filename}),预期应存在")
        else:
            logging.error(f"找到了图片元素({image.filename}),预期不应存在")
        take_screenshot(os.path.basename(image.filename))
        
    return result

def find_uiobject(
    ui_object: UiObject,
    object_name: str,
    wait_timeout=DEFAULT_TIMEOUT,
    expected=True,
) -> bool:
    """
    【Uiautomator2】基于属性定位查找
    """
    # 1. 判断元素实际是否存在
    if expected:
        # wait() 返回 True 表示找到,False 表示超时
        real_found = ui_object.wait(timeout=wait_timeout)
    else:
        # wait_gone() 返回 True 表示消失了(即不存在),False 表示还在(即存在)
        is_gone = ui_object.wait_gone(timeout=wait_timeout)
        real_found = not is_gone

    # 2. 对比预期结果
    result = not (expected ^ real_found)

    # 3. 错误处理与日志
    if not result:
        if expected:
            logging.error(f"未找到控件元素({object_name}),预期应存在")
        else:
            logging.error(f"找到了控件元素({object_name}),预期不应存在")
        
        # 处理文件名非法字符
        safe_name = object_name.replace(":", "").replace("/", "_").replace(" ", "")
        take_screenshot(safe_name)
        
    return result

# ==================== 3. 测试用例类 ====================

@allure.feature("混合驱动自动化测试Demo")
class TestHybridApp:
    
    d = None # Uiautomator2 设备对象

    def setup_class(self):
        """
        初始化:同时连接 Airtest 和 Uiautomator2
        """
        logging.info("正在初始化设备连接...")
        
        # 1. 初始化 Airtest (用于图像识别)
        # devices=["Android:///"] 自动连接本地第一台安卓设备
        auto_setup(__file__, logdir=True, devices=["Android:///"])
        
        # 2. 初始化 Uiautomator2 (用于属性定位)
        self.d = u2.connect() 
        
        # 3. 启动测试App (这里以Android原生设置为例)
        # 请替换为你自己的包名,例如: self.d.app_start("com.tencent.mm")
        self.d.app_start("com.android.settings")
        
        # 简单等待启动动画结束
        time.sleep(2)

    def teardown_class(self):
        """
        清理:关闭App
        """
        self.d.app_stop("com.android.settings")

    @allure.story("属性查找测试")
    @allure.title("使用 Uiautomator2 查找文字")
    def test_find_by_prop(self):
        """
        测试用例:验证设置页面的 'WLAN' 选项存在
        """
        # 步骤1:定义选择器 (Uiautomator2)
        # 支持 text, resourceId, description 等
        wlan_obj = self.d(text="WLAN")
        
        # 步骤2:执行断言
        is_found = find_uiobject(
            ui_object=wlan_obj,
            object_name="WLAN菜单项",
            expected=True
        )
        
        assert is_found, "断言失败:未在设置页面找到 WLAN 文字"
        
        # 步骤3:执行操作
        if is_found:
            wlan_obj.click()
            logging.info("点击成功")

    @allure.story("图像查找测试")
    @allure.title("使用 Airtest 查找图标")
    def test_find_by_image(self):
        """
        测试用例:使用图像验证页面元素
        注意:运行此用例前,请使用AirtestIDE截图并保存为 'tpl_icon.png'
        """
        # 模拟:回到设置主页
        self.d.app_start("com.android.settings") 
        time.sleep(1)

        # 步骤1:定义图片模板 (Airtest)
        # 实际使用时,请确保当前目录下有这张截图
        target_img = Template(r"tpl_icon.png", record_pos=(0, 0), resolution=(1080, 2400))
        
        # 这里的判断是为了演示,防止你没有截图导致直接报错
        if not os.path.exists("tpl_icon.png"):
            logging.warning("提示:当前目录缺少 tpl_icon.png,跳过图像识别实际执行")
            return

        # 步骤2:执行断言
        is_found = find_image(
            image=target_img,
            expected_found=True
        )
        
        assert is_found, "断言失败:未通过图像识别找到指定图标"

    @allure.story("异常测试")
    @allure.title("验证元素不存在的情况")
    def test_not_exist(self):
        """
        验证一个不存在的 ID,预期结果为 '找不到'
        """
        fake_obj = self.d(resourceId="com.android.settings:id/fake_id_12345")
        
        is_found = find_uiobject(
            ui_object=fake_obj,
            object_name="不存在的ID",
            wait_timeout=3,
            expected=False # 重点:我们期望它不存在
        )
        
        assert is_found, "断言失败:本不该存在的元素出现了"

if __name__ == "__main__":
    # 执行命令:pytest -s -v test_hybrid_demo.py --alluredir=./report
    pytest.main(["-s", "-v", "test_hybrid_demo.py", "--alluredir=./report/xml"])

9. 运行测试与生成报告

  1. 运行脚本
    在 PyCharm 终端执行:

    pytest test_hybrid_demo.py --alluredir=./report/xml
    
  2. 查看报告
    测试完成后,执行:

    allure serve ./report/xml
    

需要学习相关知识文档

airtest(只看Android和IOS相关的):airtest官方用户手册

pytest:pytest官方文档
pytest快速上手教程

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐