web自动化之PO模式

PO模式

1、为什么需要PO思想?

首先我们观察和思考一下,目前我们写的作业脚本的问题: 元素定位和操作动 作写到一起了,这就就会用导致一个问题:

  • UI的页面元素比较容易变化的,所以元素定位和脚本操作写到一起,一旦 元素发生了变化,这些脚本都需要修改
  • 特别是登录页面在很多个页面都会涉及到 被引用,那么就会导致: 维护成 本比较大。 所以,我们通过PO模式来解决这个问题。

2、什么是PO模式

Page Object Model(页面对象模型), 或者也可称之为POM。在UI自动化测试 广泛使用的一种分层设计模式。

page就是网页的页面层,object就是封装的LoginPage 类:每一个页面一个 类,包含业务逻辑和测试对象

  • 页面元素元素定位: 定义为类的属性
  • 页面的操作行为 : 定义为类的方法
  • 业务逻辑: 对页面的操作 为了得到实际结果的过程 和步骤 ,这是测试 对象

测试用例+ 测试数据层: 单独维护。 包含测试逻辑步骤和测试用例

  • 测试逻辑: 测试部分 ,预期结果和实际结果的对比 ,这是测试用例

PO核心是通过页面层封装所有的页面元素及操作,测试用例层通过调用页面层 操作组装业务逻辑。

PO模式的核心思想:体现了业务逻辑和测试逻辑 的分离,测试用例和测试对象分离

以登录操作为例:

  • 获取页面登录错误信息 登出 等操作都是业务逻辑,单独进行PageObject 的封装。
  • 用例里只传用例数据,不会出现元素定位的代码。包括断言里也不要出现 元素定位。

PO模式最终的实现效果:

页面层:

  • 页面类A(A1 A2 A3)
  • 页面类B(B1 B2 B3) 页面类C(C1 C2 C3)

用例层:

  • 用例1 = A1 + B2 + C3
  • 用例2 = A1 + B2 + C1

PO模式优点:

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

我们现在以登录页面为例:登录页面类

因为这个页面里有元素定位 + 元素操作: 把这些东西最好是封装在一起,方便 被用例层调用。

  • 页面里的元素: 写成类的属性
    • 用元组形式表示;因为后面显示等待都是用元组调用的;调用用self调 用 ;
    • 并不需要一次性把所有元素都写出来,可以后续扩展
  • 页面的操作: 封装成实例方法
    • driver先没有,
    • 可以作为参数 一些变化的数据也参数化

三层PO思想【BasePage封装思想】

但是这样写还有个问题:既然每个元素都用显示等待操作,每个页面都写一 遍太麻烦,冗余度太高了;而且每个页面都有一些其他的共同的操作,比如点 击,输入文本等;那么每个页面都重复写,是不是可以进一步优化呢?

  • 这些每个页面都会调用的方法【等待,点击,输入文本等】,就属于公共 方法; BasePage 封装思想
  • 用三层PO模式: 公共页面的内容单独提取出来封装:这也叫做 。封装成为一个BasePage类。
  • 其他页面自己独有的元素和方法依然放在单独页面类里封装;需要用到 basepage类的内容的时候,如何实现?--- 类的继承实现

所以新建一个common的目录,建一个base_page的py文件,方所有的功能 方法。

  • BasePage 用来存放所有页面类的公共部分,一些公共的操作(显式封装)
  • 封装一个BasePage的类,里面的方法都是每个页面都要用的公共的方法。

base_page.py
from time import sleep

import pyautogui
import pyperclip
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.action_chains import ActionChains

class BasePage:

    # driver每个方法都用,如果直接定位参数,没有办法实现实例方法共享,所以可以定义为实例属性 实现共享
    def __init__(self,driver): # 初始化方法里定义了以参数  到时候实例化对象传参
        """初始化函数 定义driver作为实例属性 实例实例方法共享这个实例属性"""
        self.driver = driver

    # 操作和行为 行为为实例方法
    def wait_element_clickable(self, locator):
        web_element = WebDriverWait(self.driver, 8, 0.5).until(EC.element_to_be_clickable(locator))
        return web_element

    def wait_element_visible(self, locator):
        web_element = WebDriverWait(self.driver, 8, 0.5).until(EC.visibility_of_element_located(locator))
        return web_element

    def wait_element_presence(self, locator):
        web_element = WebDriverWait(self.driver, 8, 0.5).until(EC.presence_of_element_located(locator))
        return web_element

    # 1、普通点击操作 --关键字
    def click_element(self,locator):
        self.wait_element_clickable(locator).click()

    # 2、鼠标点击操作
    def mouse_click(self,locator):
        # 先找到元素
        element = self.wait_element_visible(locator)
        # 用鼠标点击
        ActionChains(self.driver).click(element).perform()

    # 3、js点击操作
    def js_click(self,locator):
        # 先找到元素
        element = self.wait_element_visible(locator)
        # 用js传参点击
        self.driver.execute_script("arguments[0].click()",element)

    # 4、输入数据文本
    def input_text(self,locator,text):
        self.wait_element_visible(locator).send_keys(text)

    # 5、获取元素的文本 : 拿到这个文本 返回这个文本
    def get_text(self,locator):
        return self.wait_element_visible(locator).text

    # 6、获取元素的属性:拿到这个属性的值 设置为返回值
    def get_attribute(self,locator,attr_name):
        return self.wait_element_visible(locator).get_attribute(attr_name)

    # 7、窗口切换
    def switch_window(self,url):
        # 先拿到所有的窗口句柄
        handles = self.driver.window_handles
        for win in handles:
            if self.driver.current_url == url:  # 判断当前的页面是否为想要的url地址
                break
            else:
                self.driver.switch_to.window(win)  # 如果不是 继续切换

    # 8、移动鼠标
    def move_mouse(self,locator):
        element = self.wait_element_presence(locator)
        ActionChains(self.driver).move_to_element(element).perform()

    # 9、文件上传
    def file_upload(self,file_path):
        # 1、先复制路径
        pyperclip.copy(file_path)
        # 2、粘贴 -- hotkey 通过热键粘贴
        pyautogui.hotkey("ctrl", "v")
        pyautogui.press("enter", presses=2)
home_page_v1.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait

class HomePage:
    # 页面元素元素定位: 定义为类的属性 --元组
    loc_login = (By.XPATH, '//a[text()="登录"]')

    # 操作和行为 行为为实例方法
    def wait_element_clickable(self,driver,locator):
        web_element = WebDriverWait(driver, 8, 0.5).until(EC.element_to_be_clickable(locator))
        return web_element

    def wait_element_visible(self,driver,locator):
        web_element = WebDriverWait(driver, 8, 0.5).until(EC.visibility_of_element_located(locator))
        return web_element

    def wait_element_presence(self,driver,locator):
        web_element = WebDriverWait(driver, 8, 0.5).until(EC.presence_of_element_located(locator))
        return web_element

    # 点击首页里的登录链接按钮 打开登录页面 操作-- 定义为实例方法
    def click_login_link(self,driver):
        self.wait_element_visible(driver,self.loc_login).click()
test_login.py
"""
编写测试用例 执行测试用例 断言 的框架 - pytest框架
"""
from page_object.login_page_v1 import LoginPage
from page_object.home_page_v1 import HomePage
from selenium import webdriver

# pytest框架编写测试用例
def test_login():
    driver = webdriver.Chrome()
    driver.maximize_window()
    driver.get("http://mall.lemonban.com:3344/")
    # 1、点击homepage 登录的链接 == 先实例化对象,再调用实例方法,实例方法要传参driver
    HomePage().click_login_link(driver)
    # 2、调用LoginPage里的login实例方法  执行登录操作== 先实例化对象,再调用实例方法,实例方法要传参
    LoginPage().login(driver,"lemon_py","12345678")

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

24-LINUX-UDP网络连接

一UDP协议特点 TCP 协议提供的是:面向连接、可靠的、字节流服务。使用 TCP 协议通信的双发必须 先建立连接,然后才能开始数据的读写。双方都必须为该连接分配必要的内核资源,以管理 连接的状态和连接上数据的传输。TCP 连接是全双工的&…

Controlnet作者放出新的大招 IC-Light,可以操控图像生成时的光照,对内容主体重新打光生成符合新背景环境光照的图片

IC-Light代表Impose Constant Light,是一个控制图像照明的项目。可以操控图像生成时的光照,对内容主体重新打光生成符合新背景环境光照的图片。这下商品图合成这种需要最大程度保持原有主体 ID 需求的最大的问题解决了。 目前,已经发布了两种类型的模型…

多线程、进程、线程五种状态、synchronized、volatile、Lock、CAS、死锁、ThreadLocal

1、并发编程 并发编程三要素 原子性:只一个操作要么全部成功,要么全部失败可见性:一个线程对共享变量的修改,其他线程能够立刻看到有序性:程序执行的顺序按照代码的先后顺序执行 synchronized,Lock解决原…

C++ 网络编程

一、Reactor 网络编程模型 reactor 是一个事件处理模型。网络处理:因为用户层并不知道 IO 什么时候就绪,所以将对 IO 的处理转化为对事件的处理。网络模型构成: 非阻塞 IO:操作 IO,如果 IO 未就绪,IO 函数会立刻返回。IO 多路复用:检测多路 IO 是否就绪。工作流程: 注册…

【设计模式】JAVA Design Patterns——Bridge(桥接模式)

🔍目的 将抽象与其实现分离,以便二者可以独立变化。 🔍解释 真实世界例子 考虑一下你拥有一种具有不同附魔的武器,并且应该允许将具有不同附魔的不同武器混合使用。 你会怎么做? 为每个附魔创建每种武器的多个副本&…

每周刷题第三期

个人主页:星纭-CSDN博客 系列文章专栏:Python 踏上取经路,比抵达灵山更重要!一起努力一起进步! 目录 题目一:环形链表 题目二:删除有序数组中的重复项 题目三:有效的括号 题…

【C语言】程序员自我修养之文件操作

【C语言】程序员自我修养之文件操作 🔥个人主页:大白的编程日记 🔥专栏:C语言学习之路 文章目录 【C语言】程序员自我修养之文件操作前言一.文件介绍1.1为什么使用文件1.2文件分类1.3二进制文件和文本文件 二.文件的打开和关闭2.…

docker-compose Install homer

homer前言 一个非常简单的静态主页,为您的服务器保持您的服务在手,从一个简单的yaml配置文件。 前提要求 安装 docker docker-compose 参考创建一键安装homer 脚本 homer安装位置/homerhomer 脚本位置/homer/assetshomer logo 图标/home/assets/iconshomer 端口80homer 颜色…

连续三次拒绝饭局的邀请,不会在有人请你吃饭!

人们对于饭局的态度各有不同,有的认为饭局纯属浪费时间,还有各种套路,应该尽量少参加。也有的人认为饭局是沟通感情的平台,有这样的机会应该尽量去参与。不管是否喜欢饭局,但总要时不时去参加的。如果你连续三次拒绝饭…

文心智能体应用示例:职场反PUA专家的诞生

🧑 博主简介:阿里巴巴嵌入式技术专家,深耕嵌入式人工智能领域,具备多年的嵌入式硬件产品研发管理经验。 📒 博客介绍:分享嵌入式开发领域的相关知识、经验、思考和感悟,欢迎关注。提供嵌入式方向…

JAVA开发 基于最长公共子序列来计算两个字符串之间的重复率

计算两个字符串之间的重复率 最长公共子序列实现代码 最长公共子序列 基于最长公共子序列(Longest Common Subsequence, LCS)的重复率的中心逻辑是首先找到两个或多个序列中同时出现的、不一定连续但保持相对顺序的最长子序列,然后计算这个最…

知识获取概述

文章目录 知识获取研究现状技术发展趋势 知识图谱主要技术包括知识获取、知识表示、知识存储、知识建模、 知识融合、知识理解、知识运维等七个方面,通过面向结构化、半结构化和非结构化数据构建知识图谱为不同领域的应用提供支持,具体的技术架构图如下图…

全栈安全 为云而生 | 亚信安全信舱ForCloud全新品牌重磅发布

5月18日,亚信安全云安全全新品牌ForCloud正式发布。基于“全栈安全 为云而生”的创新理念,亚信安全云安全完成全新、全面、全栈升级。ForCloud的发布仪式在C3安全大会“云领未来:全栈一体化”云安全论坛上隆重举办,同时亚信安全还…

许冉直播不治本,京东需要刘强东

图片|影视剧《纸牌屋》剧照 ©自象限原创 作者丨艾AA 编辑丨薛黎 这届618,消费者的热情还未显现,商家的怒火先爆发了。 5月21日京东618开幕次日,多家图书社抵制618图书大促登上了热搜。此次争议与去年双十一京东采销与电…

执行sql脚本——kettle开发03

一、转换对象的优先级 kettle中转换和作业的执行顺序: 1、一个作业内的转换,是顺序执行的。 2、一个转换内的步骤是并行执行的。 3、作业内不支持事务,转换内支持事务。 根据业务需要,通常需要在转换内顺序执行,小技巧…

Java进阶-SpringCloud使用BeanUtil工具类简化对象之间的属性复制和操作

在Java编程中,BeanUtil工具类是一种强大且便捷的工具,用于简化对象之间的属性复制和操作。本文将介绍BeanUtil的基本功能,通过详细的代码示例展示其应用,并与其他类似工具进行对比。本文还将探讨BeanUtil在实际开发中的优势和使用…

Go微服务开发框架DMicro的设计思路

DMicro是一个基于Go语言开发的微服务开发框架,旨在简化微服务架构的开发、部署和运维过程。DMicro的设计思路主要围绕以下几个方面展开: 简化微服务开发流程 DMicro通过提供一套简洁的API和工具,使得开发者可以快速搭建微服务应用。它支持服…

景源畅信电商:抖店需要的成本高吗?

在数字化时代的浪潮中,短视频平台迅速崛起,成为连接用户与商家的新桥梁。抖音作为其中的佼佼者,不仅改变了人们的娱乐方式,也催生了新型的电商模式——抖店。许多人好奇,入驻这样一个充满活力的平台,需要承…

jwtcracker下载安装出现错误

1.jwtcracker 用于爆破jwt秘钥 2.下载 ubuntu/kali安装c-jwt-cracker及使用方法-CSDN博客 参考这个大佬写的 但是我在这里出现了这个问题 显示Cannot initialize the default message digest sha256, aborting 我实在找不出来哪里有问题,所以直接换成docker …

C++进阶:C++11(列表初始化、右值引用与移动构造移动赋值、可变参数模版...Args、lambda表达式、function包装器)

C进阶:C11(列表初始化、右值引用与移动构造移动赋值、可变参数模版…Args、lambda表达式、function包装器) 今天接着进行语法方面知识点的讲解 文章目录 1.统一的列表初始化1.1{}初始化1.2 initializer_listpair的补充 2.声明相关关键字2.1a…