UI 自动化测试框架设计与 PageObject 改造!

news2024/11/13 8:05:29

在 UI 自动化测试过程中,面对复杂的业务场景,经常会遇到这样的挑战:

  • 简单的录制/回放速度快,但无法适应复杂场景;
  • 编写自动化测试脚本比较灵活,但工作量大且可维护性差;
  • 以往的封装技术(PageObject)可以适应各种 UI 场景,但结构松散,无法在多项目中迁移;

因此,测试团队通常还需要一种定制测试框架,用以弥补现有框架的缺点。

测试框架封装思想

由于 UI 自动化测试框架围绕 UI 界面使用,因此,依旧选用 PageObject 设计模式对 UI 及测试进行封装,同时配合 Pytest 单元测试将脚本能够有效的组织、连贯应用起来,从而提高框架的可维护性和可读性。

由于测试框架基于 PageObject 设计模式,主要方向为 PO 改进,数据驱动,异常处理等,比如:

  • 测试数据的数据驱动:将数据存储到外部 yaml 文件中,利用 yaml 工具进行数据读取;
  • 数据步骤的数据驱动:将操作步骤放到外部 yaml 文件中,利用 yaml 工具对操作步骤进行读取,用专门函数解析并实现操作步骤;
  • 自动化异常处理机制:对元素查找模块进行封装和改进,包括如何处理弹窗;

Page_Object 改造

作为通用的 UI 测试框架, PageObjet 不仅适用于 Web 自动化测试,也可适用 Appium 移动自动化测试,其优点如下:

  • 减少代码重复
  • 提高测试用例可读性
  • 提高测试用例可维护性

PO 改造实例(基于雪球 App)

本案例将对雪球 App 进行 Page Objetct 封装与改进。

当启动雪球 App 时,会进入首页。点击搜索框进入搜索页,搜索某支股票然后判断股价是否大于 200:

PageObjetct 的模块关系如下,所有的模块要继承 BasePage , App 实现启动,重启,停止等操作, Main 实现进入搜索页,进入股票页等操作:

现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
分享他们的经验,还会分享很多直播讲座和技术沙龙
可以免费学习!划重点!开源的!!!
qq群号:110685036【暗号:csdn999】

base_page 模块是所有 page 类的父类,其中定义了公共方法,比如封装下面的 find 方法后,可以让子类调用 find :

from appium.webdriver.webdriver import WebDriver


class BasePage:
    _driver: WebDriver

    def __init__(self, driver: WebDriver = None):
        self._driver = driver

    def find(self, locator, value: str = None):
        #如果传进来的是tuple,只需使用一个参数:locator
        if isinstance(locator, tuple):
            return self._driver.find_element(*locator)
        else:
            return self._driver.find_element(locator, value)

App 模块封装 app 的启动,重启,停止等方法,当 app 启动时会进入 main 页面,因此在下面的 main 方法要 return Main ,Main 类的定义在后面会讲解:

from appium import webdriver
from test_appium.page.base_page import BasePage
from test_appium.page.main import Main

class App(BasePage):
    #指定app的包名和activity名
    _package = "com.xueqiu.android"
    _activity = ".view.WelcomeActivityAlias"

    def start(self):
        #如果driver为空则初始化
        if self._driver is None:
            caps = {}
            caps["platformName"] = "android"
            caps["deviceName"] = "hogwarts"
            caps["appPackage"] = self._package
            caps["appActivity"] = self._activity
            caps["noReset"] = True
            #初始化driver
            self._driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
            self._driver.implicitly_wait(30)
        #如果driver不为空,则直接启动activity
        else:
            print(self._driver)
            self._driver.start_activity(self._package, self._activity)

        return self

    def restart(self):
        pass

    def stop(self):
        pass

    def main(self) -> Main:
        #当app启动时,跳转到(实例化)Main
        return Main(self._driver)

Main 模块是首页的 PageObject ,其中的方法封装了首页的重要功能,比如下面代码中的 goto_search_page 封装了点击搜索并跳转到 Search 页:

from appium.webdriver.common.mobileby import MobileBy
from selenium.webdriver.common.by import By

from test_appium.page.base_page import BasePage
from test_appium.page.profile import Profile
from test_appium.page.search import Search


class Main(BasePage):
    #点击搜索按钮后,进入搜索页
    def goto_search_page(self):
        self.find(MobileBy.ID, "tv_search").click()
        #进入搜索页
        return Search(self._driver)

    def goto_stocks(self):
        pass

    def goto_trade(self):
        pass

    def goto_messages(self):
        pass

Search 模块可以搜索一支股票,还可以获取股票的价格,比如下图:

封装代码如下:

from appium.webdriver.common.mobileby import MobileBy
from selenium.webdriver.remote.webdriver import WebDriver


class Search:
    _driver: WebDriver

    def __init__(self, driver):
        self._driver = driver
    #输入要搜索的内容
    def search(self, key: str):
        self._driver.find_element(MobileBy.ID, "search_input_text").send_keys(key)
        self._driver.find_element(MobileBy.ID, "name").click()
        return self
    #获取股票价格,用于判断
    def get_price(self, key: str) -> float:
        return float(self._driver.find_element(MobileBy.ID, "current_price").text)

最后对上述代码建立测试,新建测试模块 test_search :

import pytest

from test_appium.page.app import App


class TestSearch:
    def setup(self):
        self.main = App().start().main()

    def test_search(self):
        assert self.main.goto_search_page().search("alibaba").get_price("BABA") > 200

以上,供大家参考,欢迎一起留言探讨。

END点赞关注不迷路!   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1229953.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

本地搭建Stackedit Markdown编辑器结合内网穿透实现远程访问

文章目录 1. docker部署Stackedit2. 本地访问3. Linux 安装cpolar4. 配置Stackedit公网访问地址5. 公网远程访问Stackedit6. 固定Stackedit公网地址 StackEdit是一个受欢迎的Markdown编辑器,在GitHub上拥有20.7k Star!,它支持将Markdown笔记保…

【AI】行业消息精选和分析(23-11-20)

技术发展 🎨 LCM即时绘画,体验所见所得: - LCM LoRA支持即时绘图生成,体验直观。 - 在线体验地址提供直接访问。 - 清华大学SimianLuo开发,加速稳定扩散模型运行。 💊 VM Pill:可吞咽装置追踪生…

CPU/GPU实现向量内积

向量内积&#xff08;点乘/点积/数量积&#xff09;&#xff1a;两个向量对应元素相乘之后求和&#xff1a; CPU实现&#xff1a; //cpu 实现一下向量内积#include<stdio.h> template<typedef T> void dot_mul(T *a, T *b, T *c, int n) { double tmp 0;for(i…

为什么同样是做测试,别人年薪30W+?我10k!!!

作为一名初出茅庐的软件测试员&#xff0c;职业发展的道路的确蜿蜒曲折&#xff0c;面对一次次的岗位竞争&#xff0c;挑战一道道的面试关卡&#xff0c;一边带着疑惑&#xff0c;一边又要做出选择&#xff0c;只能无奈的感叹&#xff1a;比你优秀的人比你还努力&#xff0c;你…

mysql主从搭建(docker)

一、主从概述 MySQL主从又叫Replication、AB复制。简单讲就是A与B两台机器做主从后&#xff0c;在A上写数据&#xff0c;另外一台B也会跟着写数据&#xff0c;实现数据实时同步。有这样几个关键点&#xff1a; 1&#xff09;MySQL主从是基于binlog&#xff0c;主上需开启binl…

“一键搜索,海量商品任你选!多平台聚合,购物更便捷!“

对于多平台聚合搜索&#xff0c;根据关键词取商品列表&#xff0c;您需要使用第三方服务或软件来实现。以下是一些可能的选择&#xff1a; 使用第三方聚合搜索工具&#xff1a;有些第三方工具可以聚合多个电商平台的商品数据&#xff0c;并提供统一的搜索接口。您可以使用这些…

机器人制作开源方案 | 钻孔植树一体化沙漠车

作者&#xff1a;徐邦国、张博宇、刘露、李晶晶、吕洁秀单位&#xff1a;天津职业技术师范大学 机械工程学院指导老师&#xff1a;何永利 摘要&#xff1a;本项目旨在设计一种专用于沙漠植树的植树车&#xff0c;以沙漠自动化植树为研究对象&#xff0c;提出一种创新式钻…

初识分布式键值对存储etcd

欢迎大家到我的博客浏览。胤凯 (oyto.github.io)大家好&#xff0c;今天我带大家来学习一下 etcd。 一、什么是 etcd etcd 是一个开源的分布式键值存储系统&#xff0c;主要用于构建分布式系统中那点服务发现、配置管理、分布式锁等场景。它采用 Raft 一致性算法来确保所有节…

Swagger(5):Swagger2常用注解

1 Api Api是类上注解。控制整个类生成接口信息的内容。 tags&#xff1a;类的名称。可以有多个值&#xff0c;多个值表示多个副本。description:描述&#xff0c;已过时。 注解如下&#xff1a; Api(tags {"MyController","Swagger学习控制器"}, desc…

详解SwinIR的论文和代码(SwinIR: Image Restoration Using Swin Transformer)

paper&#xff1a;https://arxiv.org/abs/2108.10257 code&#xff1a;https://github.com/JingyunLiang/SwinIR 目录 1. Swin Transformer layers1.1 局部注意力1.2 移动窗口机制1.3 关键代码理解 2. 整体网络结构2.1 浅层特征提取2.2 深层特征提取2.3 图像重建 3.总结 SwinI…

服务器探针-serverstatus

{alert type"info"} 之前给大家介绍过一个简单的服务器监控。uptime-kuma 今天给各位带来一个酷炫的多服务器探针和多服务器监控。ServerStatus {/alert} 作者的开源项目地址如下&#xff1a;https://github.com/cppla/ServerStatus 作者的项目体验地址如下 https://…

运动耳机什么牌子好?十大运动蓝牙耳机品牌排行榜

​运动耳机需求各有不同&#xff0c;对于我们每个人来说&#xff0c;选择最适合自己的耳机是一项重要任务。在这个耳机类型繁多&#xff0c;五花八门的时代&#xff0c;如何找到一款适合自己的运动耳机呢&#xff1f;选对运动耳机很重要&#xff0c;所以接下来安利五款相当不错…

element-ui中怎样使用iconfont的图标

1 登录 https://www.iconfont.cn/ 2 搜索合适的图 这里可以找到这个图所在的图库。这样就可以一次查找到对应的所有同款图标 3 选择同款加入购物车 4 将购物车的icon加入项目&#xff0c;注意是新建项目&#xff0c;除非你是确定需要前面已经加过的icon 5 下载icon 选择fon…

SpringBoot的启动流程

一、SpringBoot是什么&#xff1f; springboot是依赖于spring的&#xff0c;比起spring&#xff0c;除了拥有spring的全部功能以外&#xff0c;springboot无需繁琐的xml配置&#xff0c;这取决于它自身强大的自动装配功能&#xff1b;并且自身已嵌入Tomcat、Jetty等web容器&am…

课程设计:C++实现哈夫曼编码

功能实现&#xff1a; //1:先计算每个字符的权重//2&#xff1a;构建哈夫曼树//3&#xff1a;得出每个字符的哈夫曼编码。//4:根据哈夫曼编码转化为字符 代码实现&#xff1a; // 哈夫曼编码.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 //1:先计…

vue动态配置路由

文章目录 前言定义项目页面格式一、vite 配置动态路由新建 /router/utils.ts引入 /router/utils.ts 二、webpack 配置动态路由总结如有启发&#xff0c;可点赞收藏哟~ 前言 项目中动态配置路由可以减少路由配置时间&#xff0c;并可减少配置路由出现的一些奇奇怪怪的问题 路由…

如何将文字、图片、视频、链接等内容生成一个二维码?

通过二维彩虹的【H5编辑】功能&#xff0c;就可以将文字、图片、视频、文件、链接等多种格式的内容编辑在一个页面&#xff0c;然后生成一个自定义的二维码——H5编辑二维码。扫描后&#xff0c;即可查看二维码中的详细图文视频等内容了。这个功能大受欢迎&#xff01; 这个H5…

深度学习之基于CT影像图像分割检测系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 基于CT影像的图像分割检测系统可以被设计成能够自动地检测出CT图像中的病变部位或解剖结构&#xff0c;以协助医生进…

庖丁解牛:NIO核心概念与机制详解 05 _ 文件锁定

文章目录 Pre概述锁定文件 &#xff08;lock&#xff09;Code文件锁定和可移植性 Pre 庖丁解牛&#xff1a;NIO核心概念与机制详解 01 庖丁解牛&#xff1a;NIO核心概念与机制详解 02 _ 缓冲区的细节实现 庖丁解牛&#xff1a;NIO核心概念与机制详解 03 _ 缓冲区分配、包装和…

calibre更新 环境变量设置

我这里是从别的地方copy过来的calibre&#xff0c;所以不用安装。 如果需要安装请参考&#xff1a; Caibre2022.3_17版本安装及遇到问题 - 梅希的日志 - EETOP 创芯网论坛 (原名&#xff1a;电子顶级开发网) -将copy过来的calibre放在原来calibre的位置。 打开工作路径下的.b…