用户端app自动化测试(Android)-L3

一、自动化关键数据记录

1、 行为日志

  • 使用 python 自带的 logging 模块
  • 使用 pytest.ini 配置日志开关与格式
  • 参考链接
[pytest]
;日志开关 true false
log_cli = true
;日志级别
log_cli_level = info
;打印详细日志,相当于命令行加 -vs
addopts = --capture=no
;日志格式
log_cli_format = %(asctime)s [%(levelname)s] %(message)s\
         (%(filename)s:%(lineno)s)
;日志时间格式
log_cli_date_format = %Y-%m-%d %H:%M:%S
;日志文件位置
log_file = ./log/test.log
;日志文件等级
log_file_level = info
;日志文件格式
log_file_format = %(asctime)s [%(levelname)s] %(message)s \
        (%(filename)s:%(lineno)s)
;日志文件日期格式
log_file_date_format = %Y-%m-%d %H:%M:%S

2、 截图

方法名 描述
get_screenshot_as_file(filename) 保存图片为.png 格式,filename 图片路径
save_screenshot(filename) 保存图片为.png 格式,filename 图片路径
get_screenshot_as_png() 保存图片为二进制格式
get_screenshot_as_base64() 将图片保存为 base64 格式。通常用在 html 里添加截图

3、 获取页面源码

  • 通过获取页面源码,分析页面的 dom 结构
  • driver.page_source

4、 示例代码

        '''使用 xpath 定位'''
        logging.info("搜索用例")
        element = self.driver.find_element(MobileBy.XPATH,"//*[@resource-id='com.xueqiu.android:id/tv_search']")
        search_enabled = element.is_enabled()

        logging.info(f"搜索框的文本:{element.text},搜索框的坐标:{element.location},搜索框的size:{element.size}")

        if search_enabled == True:
            logging.info("点击搜索框")
            element.click()
            logging.info(f"输入搜索内容:alibaba")
            self.driver.find_element(MobileBy.XPATH,
                                     "//*[@resource-id='com.xueqiu.android:id/search_input_text']").\
                send_keys("alibaba")

            alibaba_element = self.driver.find_element(MobileBy.XPATH, "//*[@text='阿里巴巴']")

            # alibaba_element.is_displayed()
            displayed = alibaba_element.get_attribute("displayed")
            logging.info(f"是否可见:{displayed}")
            self.driver.save_screenshot("./image/search_result.png")
            assert displayed == "true"

二、app弹窗异常处理

1、 黑名单处理

  • 运行过程中不定时弹框(广告弹窗,升级提示框,新消息提示框等等)
  • 弹框不是 BUG(UI 界面提示,警告的作用)
def find(self, by, locator):
    try:
        return self.driver.find_element(by, locator)
    except Exception as e:
        for black in black_list:
            eles = self.driver.find_elements(*black)
            if len(eles) > 0:
                eles[0].click()
                return find(by, locator)
        raise e

2、 装饰器优势

  • 对原有函数的功能增强
  • 不改变原有函数的逻辑
  • 使代码更简洁、易维护

3、异常处理 - 装饰器

# Web 自动化测试 [高级]4.异常自动截图 部分
# 装饰器逻辑
def ui_exception_record(func):
    def run(*args, **kwargs):
        self = args[0]
        try:
            return func(*args, **kwargs)
        except Exception as e:
            # 这里添加所有的异常情况处理
            # 日志
            logger.warning("执行过程中发生异常")
            # 截图
            timestamp = int(time.time())
            image_path = f"./images/image_{timestamp}.PNG"
            page_source_path = \
                    f"./page_source/{timestamp}_page_source.html"
            # page_source
            with open(f"./page_source/{timestamp}_page_source.html",\
                     "w", encoding="u8") as f:
                f.write(self.driver.page_source)
            self.driver.save_screenshot(image_path)
            allure.attach.file(image_path, name="image",\
                     attachment_type=allure.attachment_type.PNG)
            allure.attach.file(page_source_path, \
                    name="page_source", \
                    attachment_type=allure.attachment_type.TEXT)
            raise e
    return run

4、 异常处理代码实现

def black_wrapper(fun):
    def run(*args, **kwargs):
        basepage = args[0]
        try:
            return fun(*args, **kwargs)
        except Exception as e:
            for black in black_list:
                eles = basepage.driver.find_elements(*black)
                if len(eles) > 0:
                    eles[0].click()
                    return fun(*args, **kwargs)
            raise e
    return run

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

三、自动化测试架构优化

1、 打造测试框架的需求与价值

  • 领域模型适配:封装业务实现,实现业务管理
  • 提高效率:降低用例维护成本,提高执行效率
  • 增强功能:解决已有框架不满足的情况

2、 自动化框架应具备的功能

  • 支持管理用例,运行用例
  • 支持查找元素/定位元素,对元素/页面 进行各种操作(点击,滑动,输入等等)
  • 支持生成测试报告
  • 能够实现功能的复用,(比如登录,搜索等)
  • 当页面有异常弹框的时候,可以进行有效的处理
  • 当用例失败,需要添加失败时的日志,截图,等信息,放在测试报告中
  • 多设备并发
  • 支持平台化

3、 自动化测试框架实现

功能 实现
管理用例,运行用例 pytest
查找元素/定位元素 Appium
测试报告 Allure
功能复用 PO 实现
异常弹框 编写代码
失败时的日志,截图 编写代码
多设备并发 selenium grid
平台化 VUE+FLASK/Django

4、 项目结构

5、 封装框架的作用

  • 复用
  • 平台化

6、 增强功能

  • 需求与价值
  • 项目结构优化
  • 框架封装

7、 项目结构优化

  • 框架层
  • 业务层
  • 用例层

8、 框架封装

  • 异常处理(弹窗黑名单)
  • 日志记录
  • 报告生成
  • 数据驱动

9、 异常处理(弹窗黑名单)

# 声明一个黑名单
def black_wrapper(fun):
    def run(*args, **kwargs):
        basepage = args[0]
        try:
            return fun(*args, **kwargs)
        except Exception as e:
            for black in black_list:
                eles = basepage.driver.find_elements(*black)
                if len(eles) > 0:
                    eles[0].click()
                    return fun(*args, **kwargs)
            raise e
    return run

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

10、 日志记录

  • 运行日志记录
  • 错误日志记录
logging.basicConfig(level=logging.INFO)


def black_wrapper(fun):
    def run(*args, **kwargs):
        basepage = args[0]
        try:
            logging.info("start find: \nargs: " + str(args) + " kwargs: " + str(kwargs))
            return fun(*args, **kwargs)
        except Exception as e:
            basepage.screenshot("tmp.png")
            with open("./tmp.png", 'rb') as f:
                picture_data = f.read()
            allure.attach(picture_data, attachment_type=allure.attachment_type.PNG)
            for black in basepage.black_list:
                eles = basepage.driver.find_elements(*black)
                if len(eles) > 0:
                    eles[0].click()
                    return fun(*args, **kwargs)
            raise e
    return run

11、报告生成

  • 异常日志
  • 异常截图
  • 测试用例步骤
  • 测试描述
  • bug,issue 关联
  • 用例分类(feature,story,step 等)

12、 参数化与数据驱动

  • 支持支持测试用例 / 步骤层级的参数化驱动配置
  • 配置方式包括三个部分
    • 参数定义(指定名字)
    • 数据源指定(指定 yaml 文件 /或者其它格式文件)
    • 数据源准备(无论是从线上环境 捞的数据,还是自己创建的测试数据)

13、 总结

  • 自动化测试框架应具备的功能
  • 自动化测试框架优化(在 PO 的基础上,添加异常处理,日志,报告 ,截图,参数化与数据驱动等)逐步的将框架完善
pip install allure-pytest
pytest test_xxx.py --alluredir=./result
allure serve ./result

14、 数据驱动

- find: //*[@text='xxxx']
  action: find_and_click
- find: //*[@text='xxxx']
  action: send
  content: 123
def load(self, yaml_path):
    with open(yaml_path, encoding="utf-8") as f:
        data = yaml.load(f)
    for step in data:
        xpath_expr = step.get("find")
        action = step.get("action")
        if action == "find_and_click":
            self.find_and_click(By.XPATH, xpath_expr)
        elif action == "send":
            content = step.get("content")
            self.send(By.XPATH, xpath_expr, content)

四、基于pageobject模式的测试框架优化实战

1、异常弹框处理

  • 定义黑名单列表
  • 处理弹框
# 声明一个黑名单
def black_wrapper(fun):
    def run(*args, **kwargs):
        basepage = args[0]
        try:
            return fun(*args, **kwargs)
        except Exception as e:
            for black in black_list:
                eles = basepage.driver.find_elements(*black)
                if len(eles) > 0:
                    eles[0].click()
                    return fun(*args, **kwargs)
            raise e
    return run

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

2、 异常截图

  • 封装screenshot()截图方法
  • 出现异常时调用截图
# 封装截图方法
def screenshot(self, path):
    self.driver.save_screenshot(path)

# 声明一个黑名单
def black_wrapper(fun):
    def run(*args, **kwargs):
        basepage = args[0]
        try:
            return fun(*args, **kwargs)
        except Exception as e:
            basepage.screenshot("tmp.png")
            for black in black_list:
                eles = basepage.driver.find_elements(*black)
                if len(eles) > 0:
                    eles[0].click()
                    return fun(*args, **kwargs)
            raise e
    return run

3、 生成报告

  • 添加日志
  • 添加截图
import logging

def black_wrapper(fun):
    def run(*args, **kwargs):
        basepage = args[0]
        try:
            logging.info(f"查找元素:{args[2]}")
            return fun(*args, **kwargs)
        except Exception as e:
            basepage.screenshot("tmp.png")
            with open("./tmp.png", 'rb') as f:
                picture_data = f.read()
            allure.attach(picture_data, attachment_type=\
                allure.attachment_type.PNG)
            for black in black_list:
                eles = basepage.driver.find_elements(*black)
                if len(eles) > 0:
                    eles[0].click()
                    return fun(*args, **kwargs)
            raise e
    return run

4、 数据驱动

  • 减少冗余代码
  • 集中管理测试数据
  • 便于维护
@pytest.mark.parametrize('参数1,参数2', [
        ('参数1-值1', '参数2-值1'),
        ('参数1-值2', '参数2-值2'),
    ])

5、 总结

  • 异常处理(弹窗黑名单),异常截图
  • 日志记录
  • 报告生成
  • 测试数据的数据驱动

五、androidwebview技术原理

六、android webview 架构与分析

七、webview 自动化测试

八、微信小程序自动化测试

1、 minium 简介

  • 为小程序专门开发的自动化框架
  • 可以进行小程序 UI 自动化测试
  • 可以进行源码函数的 mock
  • 可以直接跳转到小程序某个页面并设置页面数据
  • minium 官网地址:小程序云测-MiniTest

2、 minium 特点

  • 支持跨平台(iOS & Android)
  • 提供丰富的页面跳转方式
  • 支持获取和设置小程序页面的数据
  • 支持触发小程序元素绑定事件
  • 支持向 AppSerive 注入
  • 支持调用部分 wx 对象上的接口
  • 支持 Mock wx 对象上的接口
  • 支持 Hook wx 对象上的接口
  • 支持通过 suite 方式管理用例,config 管理配置

3、 minium 与 Appium 对比

项目 minium Appium
是否开源
是否跨平台
支持语言 Python,JavaScript Java, Python, JavaScript 等
底层原理 小程序调试基础库 WebSocket 协议,CS 架构
实施成本
作用范围 渲染层和逻辑层 渲染层
持续集成 支持 支持
社区支持 腾讯 thoughtworks 公司

4、 运行环境

  • 安装 python3.8(以上版本)
  • 微信开发者工具下载:
  • 安装 minium 库
    • pip3 install https://minitest.weixin.qq.com/minium/Python/dist/minium-latest.zip
  • 环境检查
    • minitest -v

5、 微信开发者工具介绍

6、代码示例

import minium

class TestMinium(minium.MiniTest):
    def setUp(self):
        self.mini = minium.Minium({
            # 项目绝对路径
            "project_path":\
            "/Users/juanxu/Documents/小程序实战代码/miniprogram-demo",
            # 替换成你的【开发者工具cli地址】
            #macOS: <安装路径>/Contents/MacOS/cli
            # Windows: <安装路径>/cli.bat
            "dev_tool_path": "/Applications/wechatwebdevtools.app/\
            Contents/MacOS/cli",

        })

    def test_demo(self):
        sys_info = self.mini.get_system_info()
        print(sys_info)
        print(sys_info.get("SDKVersion"))

7、 minium 基本使用

  • Minium:负责初始化整个自动化框架
    • Minium():初始化函数
  • App:提供小程序应用层面的各种操作, 包括页面跳转, 获取当前页面, 页面栈等功能
    • screen_shot():截图
    • go_home():跳转到小程序首页
    • redirect_to(): 直接打开某个页面
  • Page:提供了小程序页面内包括 set data, 获取控件, 页面滚动等功能
    • get_element(‘css 或 xpath 表达式’):在当前页面查询控件
    • click():点击元素
    • input(‘输入值’):输入
    • inner_text:获取元素文本
    • attribute():获取元素属性

8、 打开页面

  • redirect_to 直接打开某个页面
import minium
class TestMinium(minium.MiniTest):
    def setUp(self):
        self.mini = minium.Minium({
 "project_path":\
 "/Users/juanxu/Documents/小程序实战代码/miniprogram-demo",
            "dev_tool_path": "/Applications/wechatwebdevtools.app/Contents/MacOS/cli",
        })
    def test_redirect(self):
        """直达某个页面"""
        # miniprogram/app.json 文件中定义了路径
        # 首页
        self.app.redirect_to("/page/component/index")
        time.sleep(2)
        # 小程序云开发展示
        self.app.redirect_to("/page/cloud/index")
        time.sleep(2)
        # 小程序扩展能力展示
        self.app.redirect_to("/page/extend/index")
        time.sleep(2)

9、 元素定位

  • 只支持 CSS, XPath 表达式
选择器 样例 样例描述
.class .intro 选择所有拥有 class=“intro” 的组件
#id #firstname 选择拥有 id=“firstname” 的组件
tagname view 选择所有 view 组件
tagname, tagname view, checkbox 选择所有文档的 view 组件和所有的 checkbox 组件

10、 元素定位表达式

  • 通过 css/xpath 表达式定位: get_element(selector)
  • 通过标签中间文本定位: get_element(selector, inner_text)
  • 父结点定位子结点: get_element("selector").get_element("selector")

11、 元素属性

方法名 说明
size 元素的宽高
inner_text 元素标签中间的文字

12、 元素操作

方法名 说明
click() 点击操作
input() 输入
get_element() 获取元素
attribute() 获取元素属性

13、 关键数据验证

方法名 说明
screen_shot(test_screen.png) 截图
logger.info(日志) 日志

14、 minium 实战

  • 打开 demo 首页
  • 点击【表单组件】
  • 点击【input】
  • 向第一个输入框中输入【python 语言】
  • 向第二个输入框中输入【aaa】
  • 向第三个输入框中输入【bbb】
  • 验证第三次输入的内容【bbb】
class TestMinium(minium.MiniTest):
    def setUp(self):
        self.mini = minium.Minium({
            "project_path":\
            "/Users/juanxu/Documents/霍格沃兹测试学院/02录播课程代码/\
            录播需要上传的课程代码/小程序实战代码/miniprogram-demo",
            "dev_tool_path": \
            "/Applications/wechatwebdevtools.app/Contents/MacOS/cli",

        })
    def test_element_operate(self):
        name = "hogwarts霍格沃兹"
        self.page.get_element("view",inner_text="表单组件").click()
        time.sleep(2)
        self.page.get_element("navigator.navigator",inner_text="input").click()
        self.page.get_element("//input[@placeholder='将会获取焦点']").input(name)
        time.sleep(2)
        self.page.get_element("//input[@placeholder=\"最大输入长度为10\"]").input("123456")
        self.page.get_element("//input[@placeholder=\"输入同步到view中\"]").input("bbbbb")
        eles = self.page.get_elements("view.weui-cells__title")
        ele = eles[2]
        input = ele.inner_text
        ele1 = self.page.get_element("//input[@placeholder='将会获取焦点']")
        print(ele1.attribute("value"))
        self.app.screen_shot("test_screen.png")
        assert "bbbbb" in input

15、 总结

  • 优点:官方开源,使用便捷,
  • 缺点:定制化差,必须要源码
  • 适用场景:
    • 必须要有项目源码
    • 产品不能太复杂

九、手机浏览器自动化测试