PO模式在Selenium中简单实践

news2024/11/18 14:55:51

初识PO模式

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

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

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

优点

PO模式把页面元素定位和业务操作流程分开,界面元素的变化则不需要修改业务逻辑代码
PO能提高代码的可读性,高复用性,可维护性
设计准则
1.使用公共方法来代表页面提供的服务

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

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

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

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

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

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

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 测试报告

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

学习安排上

如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。

在这里插入图片描述

视频文档获取方式:

这份文档和视频资料,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!以上均可以分享,点下方小卡片进群即可自行领取

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

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

相关文章

InstructGPT笔记

一、InstructGPT是在GPT3上微调,ChatGPT是在GPT3.5上微调 二、该论文展示了怎么样对语言模型和人类意图之间进行匹配,方法是在人类的反馈上进行微调。 **三、方法简介:**收集很多问题,使用标注工具将问题的答案写出来&#xff0…

大数据面试题flume篇

1.Flume 的Source,Sink,Channel 的作用?你们Source 是什么类型? 1. 作用 (1)Source组件是专门用来收集数据的,可以处理各种类型、各种格式的日志数据,包括 avro、thrift、exec、jm…

Nginx介绍及安装(windows版,Linux版)

目录 一、Nginx介绍 1、Nginx优势 2、Nginx作用 3、部署静态资源 4、代理 5、负载均衡 二、Nginx安装步骤(windows版) 三、Nginx安装步骤(Linux版) 1、官网下载安装包,下载完之后上传到Linux系统上 2、在Lin…

股票量化策略是如何被执行出来的?

在股票量化方面,很多投资者是不知道怎么样挖掘量化策略,便在量化交易接口方面会通过股票交易接口将数据慢慢挖掘出来,就简单的通过api接口调用数据方面,直接通过交易接口端输出交易持仓数据,并且通过交易系统对数据的筛…

什么是“镜像浏览”?文件夹加密后的镜像浏览有什么用?

电脑中的文件夹经常用来储存各种重要文件,加密保护成为很多人的选择,而夏冰加密软件拥有各种适用于不同场景的文件夹加密软件,备受用户喜爱。在我们打开加密文件夹之后,我们可以在加密控制面板中发现“镜像浏览”的按钮&#xff0…

Sharding-jdbc

一、概念理解垂直切分:包含垂直分库和垂直分表1.1、垂直分库 :专库专用(按照业务类型对表分类)1.2、垂直分表:基于数据表的列(字段)为依据切分的,是一种大表拆小表的模式。1.3、垂直…

【Python--torch(激活函数说明+代码讲解)】激活函数(sigmoid/softmax/ELU/ReLU/LeakyReLU/Tanh)

【Python–torch】激活函数(sigmoid/softmax/ELU/ReLU/LeakyReLU/Tanh) 文章目录【Python--torch】激活函数(sigmoid/softmax/ELU/ReLU/LeakyReLU/Tanh)1. 介绍2. 常用激活函数说明2.1 Sigmoid2.1.1 公式2.1.2 图像2.1.3 代码解读2.2 Softmax2.2.1 公式2.2.2 代码解读2.3 ELU2.…

荧光探针Pyrene-PEG2-Propargyl,芘甲酰胺-二聚乙二醇-丙炔

Pyrene-PEG2-Propargyl物理参数: CAS号:N/A | 英文名:Pyrene-PEG2-Propargyl |中文名:芘甲酰胺-二聚乙二醇-丙炔分子式:C24H21NO3分子量:371.44纯度标准:95%外形颜色:淡黄色或白色固…

shell学习4

目录 一、统计文本中的词频 二、压缩javascript 三、打印文件的或行中的第n个单词或列---awk 3.1 利用awk打印文件中每行中的第五个单词。 3.2 利用awk打印当前目录下的文件的权限和文件名 3.3 利用awk打印从M行到N行这个范围内的所有文本 3.4 利用awk 部分提取文件中的内…

opencv复习

文章目录图像衡量结果(损失函数)预测的好坏前向传播 反向传播图像 实质是矩阵 长 宽 像素通道(0-255 0 黑 255 亮) 假设这里做一个10分类 行向量✖列向量是一个数 分类 最后的结果是一个各个分类的概率值 这里的b是偏置项&…

学校节能降耗减排方案——能耗监管平台的建设及效果剖析

摘要:作为崭新的校园能耗管理手段,能耗监测平台以传统管理方式无法企及的优势有力地提升了高校能源管理工作的水平.从而受到了相关管理者的青睐。本文梳理总结了高校能耗监测平台的基本组成和优势特点,同时对能耗平台建设和使用中…

nginx设置重定向跳转后ip:[端口]/abc变成ip/abc而报错404

nginx设置重定向跳转后 ip:[端口]/abc 变成 ip/abc 而报错404nginx配置:server {listen 80;server_name _;client_max_body_size 300m;absolute_redirect off;location / {root html;index index.html index.htm;proxy_set_header X-Real-IP $remote_a…

【nodejs-04】黑马nodejs学习笔记04-MySQL简介及安装

文章目录1.数据库的基本概念1.1什么是数据库1.2 常见的数据库及分类1.3 传统型数据库的数据组织结构2.安装并配置MySQL2.1 了解需要安装哪些MySQL相关的软件2.2 MySQL 在 Mac 环境下的安装2.3 MySQL 在 Windows 环境下的安装1.数据库的基本概念 1.1什么是数据库 数据库&#x…

JavaWeb学习

文章目录Tomcat 详解1 Tomcat 安装2 默认端口号3 面试题4 编写与发布一个网站Http 详解1 http 请求2 http 响应3 面试题Tomcat 详解 1 Tomcat 安装 进入Tomcat官网下载压缩包:https://tomcat.apache.org/ 将压缩包解压即可直接使用 启动Tomcat:bin目录下startup.b…

【软件测试】如何在测试团队中工作游刃有余?你的测试技巧......

目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言 在测试团队中&#…

基于数据库实现分布式锁

分布式锁概述 前言 我们的系统都是分布式部署的,日常开发中,秒杀下单、抢购商品等等业务场景,为了防⽌库存超卖,都需要用到分布式锁。 分布式锁其实就是,控制分布式系统不同进程共同访问共享资源的一种锁的实现。如…

xshell6运行报错:由于找不到mfc110u.dll、MSVCR110.dll无法继续执行代码

今天给大家分享一下我刚装完系统遇到得问题,由于新盟的罗建雨【胡巴】老师帮我给电脑加了固态,又重装了系统,因此电脑里面得所有软件需要重装,在我重装的过程中遇到了一个小问题给大家分享一下,如果大家以后遇到也方便解决。 问题: 安装Xshell时电脑系统报错:“由于找…

一、微服务架构介绍

目录 一、微服务架构介绍 二、出现和发展 三、传统开发模式和微服务的区别 四、微服务的具体特征 五、SOA和微服务的区别 1、SOA喜欢重用,微服务喜欢重写 2、SOA喜欢水平服务,微服务喜欢垂直服务 3、SOA喜欢自上而下,微服务喜欢自下…

为什么项目的时间跟踪管理很重要 ?

项目通常被分解为需要完成的任务,以实现项目目标。时间跟踪可以帮助你了解每项任务需要多长时间,从而使你更准确地估计未来的项目。 除此以外,跟踪项目时间还有以下令人难以置信的好处: 1、提高生产力 通过记录在每项任务上花…

项目经理为什么要做时间管理?

对于时间的管理,有人做不到,有人不知道,对每一个成功的人来说,时间管理是很重要的一环。 对于项目经理而言,由于项目经理每天要在项目上花费大量的时间,所以个人时间比较少,因此项目经理就需要安…