初识PO模式并在Selenium中简单实践

news2024/11/25 13:20:08

初识PO模式

PO(PageObject)是一种设计模式。简单来说就是把一些繁琐的定位方法、元素操作方式等封装到类中,通过类与类之间的调用完成特定操作。

PO被认为是自动化测试项目开发实践的最佳设计模式之一。

在学习PO模式前,可以先复习一下面向对象的编程思想。我觉得两者很像。

优点

  • PO模式把页面元素定位和业务操作流程分开,界面元素的变化则不需要修改业务逻辑代码
  • PO能提高代码的可读性,高复用性,可维护性

设计准则

1.使用公共方法来代表页面提供的服务

2.不要暴露页面的内部细节(比如元素、元素的定位方法等),隔离测试用例和业务和页面对象

3.PO本身通常不应进行断言或判断。判断和断言是测试的一部分,而不是在PO中。

4.PO不一定需要代表整个界面,而是在测试中‘用到什么写什么’

5.相同的操作,但是数据不同,带来的不同结果可以封装成不同的方法。

6.方法可以返回其他的页面对象,进行页面的关联。

以上是比较官方的PO设计准则,我们需要根据具体业务的实际情况决定是完全遵循还是部分遵循。

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

selenium中的分层模型

  • 表现层:页面中可见的元素,都属于表现层。(元素定位器的编写)
  • 操作层:对页面可见元素的操作。(点击、输入文本等)
  • 业务层:上面2层的组合,联合到一起形成某个业务动作。
  • 测试用例:组合了一个或多个页面的方法,操作对应的元素,完成的测试。

PO模式实战

接下来就用PO模式完成一个简单的‘百度登录模块’的测试

思路:

1.创建一个elements.py存放登录界面所有的元素定位方式(用到哪个写哪个)

2.创建一个common_driver.py存放一些共用的浏览器相关方法

3.创建一个common_basepage.py存放共用的元素操作方法

4.创建一个test_cases.py文件存放测试用例

以下为部分代码:

  • 找到我们测试登录模块需要操作到的元素,将其定位方法写到elements.py中
#elements.py

class Elements():
    '''存放用到的所有元素定位器'''

    #登录前的界面元素
    LOGIN_BUTTON_OUT = ('id','s-top-loginbtn')#百度首页的‘登录’按钮
    LOGIN_WIN = ('id','TANGRAM__PSP_4__content')#登录窗口
    USERNAME_INPUT = ('id','TANGRAM__PSP_11__userName')#输入账号栏
    PASSWORD_INPUT = ('id','TANGRAM__PSP_11__password')#输入密码栏
    LOGIN_BUTTON_IN = ('id','TANGRAM__PSP_11__submit')#登录界面的‘登录’按钮

    #登录后的界面元素
    USER_INFO = ('css selector','#s-top-username > span.user-name.c-font-normal.c-color-t')#右上角的用户信息
    QUIT_BOTTON = ('css selector','#s-user-name-menu>.quit')#退出登录按钮
  • 浏览器相关操作放到common_driver.py中
# common_driver.py

from selenium import webdriver
from environment_config import Env

class Single(object):
    '''
    设计单例模式
    '''
    _instance = None #实例
    def  __new__(cls, *args, **kwargs):
        if cls._instance is None:     #此处是可以用__instance
            cls._instance = super().__new__(cls)
        return cls._instance
class Open_Driver(Single):
    '''
    打开一个浏览器
    '''
    driver = None
    def get_driver(self,browser_type=Env.BROWSER_TYPE,headless_flag=Env.HEADLESS_FLAG):
        '''
        根据参数打开想要的浏览器
        :param browser_type: 浏览器类型,读取Env文件中的值作为默认值
        :param headless_flag: 是否有头,读取Env文件中的值作为默认值,True/False
        :return: 返回一个浏览器对象
        '''
        if self.driver is None:
            if not headless_flag:#如果是有头模式
                if browser_type == 'chrome':
                    self.driver = webdriver.Chrome()
                elif headless_flag == 'firefox':
                    self.driver = webdriver.Firefox()
                else:
                    raise Exception(f'暂不支持{browser_type}浏览器')
            else:#如果是无头模式
                _option = webdriver.ChromeOptions()
                _option.add_argument('--headless')#添加无头模式参数'--headless'
                if browser_type == 'chrome':
                    self.driver = webdriver.Chrome(options=_option)
                elif headless_flag == 'firefox':
                    self.driver = webdriver.Firefox(options=_option)
                else:
                    raise Exception(f'暂不支持{browser_type}浏览器')
            self.driver.maximize_window()#窗口最大化
            self.driver.implicitly_wait(Env.IMPLICITLY_WAIT_TIME)#隐式等待,读取Env文件中IMPLICITLY_WAIT_TIME的值
        return self.driver #返回浏览器对象
  • 把要用到的元素操作方法写入到common_basepage.py中
# common_basepage.py
from common_driver import Open_Driver

class BasePage():
    '''
    存放所有界面元素操作方法
    '''
    def __init__(self):
        self.driver = Open_Driver().get_driver()

    def open_url(self,url):
        '''打开网址'''
        self.driver.get(url)
    def get_element(self,locator):
        '''
        定位元素
        :param locator:元素定位器,从elements中取
        :return: 元素对象
        '''
        return self.driver.find_element(*locator)

    def input_text(self,locator,text,append=False):
        '''
        在元素上输入文本
        :param locator: 元素定位器
        :param text: 要输入的文本
        :param append: 是否先清空,默认清空
        '''
        if append:#不需要清空内容,追加写入
            self.driver.find_element(*locator).send_keys(text)
        else:#先清空,再写入
            self.driver.find_element(*locator).clear()
            self.driver.find_element(*locator).send_keys(text)
    def click_element(self,locator):
        '''
        点击元素
        :param locator: 元素定位器
        '''
        self.driver.find_element(*locator).click()

    def ele_find_ele_input(self,locator1,locator2,text):
        '''
        在元素1上找元素2
        :param ele1: 元素1
        :param ele2: 元素2
        :return: 元素2
        '''
        return self.driver.find_element(*locator1).find_element(*locator2).send_keys(text)
    def get_element_text(self,locator):
        return self.driver.find_element(*locator).text
  • 页面对象loginpage.py
from common_basepage import BasePage
from datas import Datas
from elements import Elements
from logsuccesspage import LogSuccessPage

class LoginPage(BasePage):
    def open_loginpage(self,url):
        '''
        打开登录页
        :param url:登录页url
        :return: LoginPage实例对象
        '''
        self.open_url(url)
        return self
    def login_baidu(self,username,password):
        '''
        登录百度账号
        :param username: 用户名
        :param password: 密码
        :return: 登录成功后的页面对象
        '''
        self.click_element(Elements.LOGIN_BUTTON_OUT)#点击右上角登录
        self.ele_find_ele_input(Elements.LOGIN_WIN,Elements.USERNAME_INPUT,Datas.USERNAME)#输入账号
        self.ele_find_ele_input(Elements.LOGIN_WIN,Elements.PASSWORD_INPUT,Datas.PASSWORD)#输入密码
        self.click_element(Elements.LOGIN_BUTTON_IN)
        return LogSuccessPage()
  • 测试用例test_cases.py
from time import sleep
import pytest
from datas import Datas
from elements import Elements
from environment_config import Env
from loginpage import LoginPage

class Test_login():
    def test_login01(self):
        '''
        登录成功的测试
        :return:
        '''
        test_page = LoginPage()#创建实例
        test_page.open_loginpage(Env.TEST_URL)#打开测试url
        new_page=test_page.login_baidu(Datas.USERNAME,Datas.PASSWORD)#登录百度账号
        sleep(2)
        text = new_page.get_element_text(Elements.USER_INFO)#登录成功界面
        assert text == 'yvvgfffvbh'#断言用户名称是否正确

if __name__ == '__main__':
    pytest.main(['-vs'])
  • 运行结果

写完花了4个小时,感受就是:

1.要理解透彻Python中的面向对象思想。

2.写完整体结构后要继续优化。

可以看到,我们所有数据都放在配置文件中,代码中不会暴露任何的界面元素或账号数据。 最后用pytest执行测试用例即可。

以上只是一个最简版的PO模型项目。只是遵循了po设计准则,并不完整。

一个完整的selenium测试项目大体上应该包括:

1.tools 工具类,格式转换、路径操作等

2.commom 基类,一些公用的方法

3.pageobjects 页面对象类

4.testcases 测试用例

5.test_datas 测试数据,yaml/Excel文件等

6.outfiles 输出文件,log和截图等

7.testreport 测试报告

项目结构并没有具体标准,分类清晰即可。重要的是在设计过程中遵循上文说到的’设计准则‘。

 

最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走! 希望能帮助到你!【100%无套路免费领取】

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

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

相关文章

HeteroTiC: 基于异构时间通道的鲁棒网络流水印

文章信息 论文题目:HeteroTiC: A robust network flow watermarking based on heterogeneous time channels 期刊(会议):Computer Networks 时间:2022 级别:CCF B 文章链接:https://doi.org/10…

堆在排序中的应用

堆排序 1、堆排序原理 堆排序是利用到了堆这种数据结构,我们首先回顾一下二叉堆的特性: 最大堆的堆顶是整个堆中的最大元素。最小堆的堆顶是整个堆中的最小元素。 以最大堆为例,如果删除一个最大堆的堆顶(并不是完全删除&…

YOLOv5改进之ShuffleNetV2

目录 一、原理 网络结构 二、代码 三、应用到YOLOv5 一、原理

关于神经网络,你不得不知的三大要点

什么是神经网络? 神经网络是一个具有相连节点层的计算模型,其分层结构与大脑中的神经元网络结构相似。神经网络可通过数据进行学习,因此,可训练其识别模式、对数据分类和预测未来事件。 神经网络将您的输入细分为多个抽象层。比…

班级管理经验分享

班级管理经验如同作战策略,需要不断摸索和实践。以下是我结合自身经历,总结出的一些班级管理经验,与大家分享。 一、了解学生 作为班主任,首先要了解每个学生的性格、爱好、学习情况、家庭背景等。这需要我们与学生建立良好的沟通…

C++——取地址和自增运算

今天做了道题一下给我整蒙了,把实验探索发篇文章,题目如下: int a[]{10,11,12},*p&a[0];执行完*p;*p1;后a[0],a[1],a[2]的值各为多少 答案是 10, 12, 12怎么理解*p,*p1呢,我觉得可以用汉语把代码读一下,p&#xf…

Make sure that using this pseudorandom number generator is safe here.

问题类型:安全热点 安全问题级别:MEDIUM 一、问题代码 工具类Package: Java commons-lang3 库 RandomUtils 随机数工具类 import org.apache.commons.lang3.RandomUtils; 用法: RandomUtils.nextInt(0, 999999999) //生成 0…

记一次深入内核的数据库高并发性能优化实践

前不久,我们接到客户长江电力的反馈,称在生产环境中进行高并发查询,例如包含数百个测点的近千个并发作业,在从近三月的数据中取数或聚合计算时,会出现作业超时,但CPU利用率却很低。 接到反馈后&#xff0c…

快速操控鼠标行为!Vue鼠标按键修饰符让你事半功倍

🎬 江城开朗的豌豆:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 📝 个人网站 :《 江城开朗的豌豆🫛 》 ⛺️ 生活的理想,就是为了理想的生活 ! ⭐ 专栏简介 欢迎来到前端入门之旅!这个…

【Qt之QSqlRelationalTableModel】描述及使用

描述 QSqlRelationalDelegate链接: https://blog.csdn.net/MrHHHHHH/article/details/134690139 QSqlRelationalTableModel类为单个数据库表提供了一个可编辑的数据模型,并支持外键。 QSqlRelationalTableModel的行为类似于QSqlTableModel,但允许将列设…

解锁Jira本地部署的数据中心版高级功能,打造高效、智能、精细化的项目管理

近日,在龙智携手Atlassian与JFrog共同举办的“大规模开发创新:如何提升企业级开发效率与质量”的线下研讨会中,龙智高级咨询顾问、Atlassian认证专家叶燕秀为大家带来了精彩演讲,解锁Jira Data Center版的诸多高级功能&#xff0c…

RLHF:强化学习结合大预言模型的训练方式

RLHF (Reinforcement Learning from Human Feedback) 以强化学习方式依据人类反馈优化语言模型。 文章目录 一、简介二、一般的流程三、微调gpt介绍示例 参考文章 一、简介 强化学习从人类反馈中学习(RLHF,Reinforcement Learning from Human Feedback&a…

6 Redis缓存设计与性能优化

缓存穿透 缓存穿透是指查询一个根本不存在的数据, 缓存层和存储层都不会命中, 通常出于容错的考虑, 如果从存储层查不到数据则不写入缓存层。缓存穿透将导致不存在的数据每次请求都要到存储层去查询, 失去了缓存保护后端存储的意义…

Linux CentOS7 安装Docker | 中文输入法

CentOS7安装中文输入法: 在安装CentOS时,我们为了方便使用,语言选择了中文,但是我们发现,在Linux命令行或者是浏览器中输入时,我们只能输入英文,无法输入汉字。 用yum 安装ibus 命令&#xff…

05 # 基本类型

类型注解 作用&#xff1a;相当于强类型语言中的类型声明 语法&#xff1a; (变量/函数): type 原始类型: let bool: boolean true; let num: number 313; let str: string kaimo;数组: let arr: number[] [1, 2, 3]; let arr2: Array<number | string> [1, 2,…

【数据库】基于索引的扫描算法,不同类型索引下的选择与连接操作,不同的代价及优化

基于索引的算法 ​专栏内容&#xff1a; 手写数据库toadb 本专栏主要介绍如何从零开发&#xff0c;开发的步骤&#xff0c;以及开发过程中的涉及的原理&#xff0c;遇到的问题等&#xff0c;让大家能跟上并且可以一起开发&#xff0c;让每个需要的人成为参与者。 本专栏会定期更…

WordPress更改文章分类插件

当WP网站内容比较多的时候&#xff0c;有时候如果涉及到批量修改文章分类&#xff0c;如果一个个的去操作的话就太费事了&#xff0c;如果使用后台批量修改分类的话是增加旧分类不会取消选择&#xff0c;就就导致我们适得其反还需要一个一个的去编辑取消&#xff0c;实在繁琐了…

机器人与3D视觉 Robotics Toolbox Python 一 安装 Robotics Toolbox Python

一 安装python 库 前置条件需要 Python > 3.6&#xff0c;使用pip 安装 pip install roboticstoolbox-python测试安装是否成功 import roboticstoolbox as rtb print(rtb.__version__)输出结果 二 Robotics Toolbox Python样例程序 加载机器人模型 加载由URDF文件定义…

Pinia仓库统一管理

pinia独立维护 在src/stores文件夹下创建index.js文件&#xff0c;将main.js中关于pinia的语句放到index.js中 index.js文件内容&#xff1a; import { createPinia } from pinia import piniaPluginPersistedstate from pinia-plugin-persistedstate const pinia createPi…

电商API接口的接入|京东商品API接口接入说明

京东联盟商品接口API申请&#xff1a; 一、京东联盟 - 注册/登录 1、网址&#xff1a;https://union.jd.com 2、首次登录请先注册&#xff0c;注册成功后即可登录&#xff0c;需要完善个人信息&#xff0c;用于佣金结算。如果有京东商城的账号&#xff0c;也可以直接登录。 …