day16-测试自动化之selenium的PO模式

news2024/9/23 11:21:44

一、PO模式介绍       

        PO(Page Object)模式是一种在自动化测试中常用的设计模式,将页面的每个元素封装成一个对象,通过操作对象来进行页面的交互。

        一般分为六个版本,现在大部分企业都用的V4版本,三层结构(base+page+scripts)

        V1:不使用任何设计模式和单元测试框架

                问题:无法批量运行

                代码例子

# 导包
from time import sleep
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait

# 获取浏览器对象
chromedriver_path = r"C:\Program Files\Google\Chrome\Application\chromedriver.exe"
service = Service(executable_path=chromedriver_path)
driver = webdriver.Chrome(service=service)

def main():
    # 打开页面
    driver.get("http://www.tpshop.com/index.php")

    # 网页最大化
    driver.maximize_window()

    # 隐式等待
    driver.implicitly_wait(30)

    # 点击登录链接
    driver.find_element(By.CSS_SELECTOR,"body > div.tpshop-tm-hander > div.top-hander.clearfix > div > div > div.fl.nologin > a.red").click()

    # 输入用户名
    driver.find_element(By.CSS_SELECTOR,"#username").send_keys("admin")

    # 输入密码
    driver.find_element(By.CSS_SELECTOR,"#password").send_keys("123456")

    # 输入验证码
    driver.find_element(By.CSS_SELECTOR,"#verify_code").send_keys("8888")

    # 点击登录按钮
    driver.find_element(By.CSS_SELECTOR,"#loginform > div > div.login_bnt > a").click()

    # 获取错误提示信息
    msg = driver.find_element(By.CSS_SELECTOR,".layui-layer-content").text

    # 断言
    # assert msg == "用户名不存在!"
    # assertNotIn()

    # 点击提示框确定按钮
    driver.find_element(By.CSS_SELECTOR,".layui-layer-btn0").click()

    # 暂停两秒钟
    sleep(2)

    # 关闭浏览器驱动
    driver.quit()


if __name__ == '__main__':
    main()

        V2:使用UnitTeSt管理用例

                问题:业务脚本没有与页面对象分开

        V3:使用方法封装的思想,对代码进行优化

                问题:代码冗余量太大了

        V4:采用PO模式的分层思想对代码进行拆分

                结构:

                        base(基类):page页面一些公共的方法;

                                1.初始化方法

                                2.查找元素方法

                                3.点击元素方法

                                4.输入方法

                                5.获取文本方法

                                6.截图方法

                                扩展:loc变量:类型为元组:*loc为解包

                                注意:

                                1.以上方法封装时候,解包只需1此,在查找元素解包

                                2.driver为虚拟,谁调用base时,谁传入,无需关注从哪里来

                                3.loc:真正使用1oc的方法只有查找元素方法使用

                                代码                

import time
import page
from selenium.webdriver.support.wait import WebDriverWait
from base.get_logger import GetLogger

# 获取log日志器
log = GetLogger().get_logger()


class Base:

    def __init__(self, driver):
        log.info("[base]: 正在获取初始化driver对象:{}".format(driver))
        self.driver = driver

    # 查找元素方法 封装
    def base_find(self, loc, timeout=30, poll=0.5):
        log.info("[base]: 正在定位:{} 元素,默认定位超时时间为: {}".format(loc, timeout))
        # 使用显示等待 查找元素
        return WebDriverWait(self.driver,
                             timeout=timeout,
                             poll_frequency=poll).until(lambda x: x.find_element(*loc))

    # 点击元素 方法封装
    def base_click(self, loc):
        log.info("[base]: 正在对:{} 元素实行点击事件".format(loc))
        self.base_find(loc).click()
        # self.driver.execute_script("arguments[0].click();", self.base_find(loc))

    # 输入元素 方法封装
    def base_input(self, loc, value):
        # 获取元素
        el = self.base_find(loc)
        # 清空
        log.info("[base]: 正在对:{} 元素实行清空".format(loc))
        el.clear()
        # 输入
        el.send_keys(value)

    # 获取文本信息 方法封装
    def base_get_text(self, loc):
        log.info("[base]: 正在获取:{} 元素文本值".format(loc))
        return self.base_find(loc).text

    # 截图 方法封装
    def base_get_image(self):
        log.info("[base]: 断言出错,调用截图")
        self.driver.get_screenshot_as_file("./image/{}.png".format(time.strftime("%Y_%m_%d %H_%M_%S")))

    # 判断元素是否存在 方法封装
    def base_element_is_exist(self, loc):
        try:
            self.base_find(loc, timeout=5)
            log.info("[base]: {} 元素查找成功,存在页面".format(loc))
            return True  # 代表元素存在
        except Exception as e:
            log.error("[base]:发生错误{},{} 元素查找失败,不存在当前页面".format(e, loc))
            return False  # 代表元素不存在

    # 回到首(页购物车、下订单、支付)都需要用到此方法
    def base_index(self):
        time.sleep(5)
        self.driver.get(page.URL)

    # 切换frame表单方法
    def base_switch_frame(self, name):
        self.driver.switch_to.frame(name)

    # 回到默认目录方法
    def base_default_content(self):
        self.driver.switch_to.default_content()

    # 切换窗口 方法 调用此方法
    def base_switch_to_window(self, title):
        log.info("正在执行切换title值为:{}窗口 ".format(title))
        self.driver.switch_to.window(self.base_get_title_handle(title))

    # 获取指定title页面的handle方法
    def base_get_title_handle(self, title):
        # 获取当前页面所有的handles
        for handle in self.driver.window_handles:
            log.info("正在遍历handles:{}-->{}".format(handle, self.driver.window_handles))
            # 切换 handle
            self.driver.switch_to.window(handle)
            log.info("切换 :{} 窗口".format(handle))
            # 获取当前页面title 并判断 是否等于 指定参数title
            log.info("判断当前页面title:{} 是否等于指定的title:{}".format(self.driver.title, title))
            if self.driver.title == title:
                log.info("条件成立! 返回当前handle{}".format(handle))
                # 返回 handle
                return handle

                        page(页面对象):一个页面封装成一个对象;

                                应用:继承base;

                                实现:

                                        1.模块名:page+实际操作模块名称 如:page_login.py

                                        2.页面对象名:以大驼峰方法将模块名抄进来,有下划线去掉下划线

                                        3.方法:涉及元素,将每个元素操作单独封装一个操作方法

                                        4.组装:根据需求组装以上操作步骤

                                代码

from base.base import Base
import page
from base.get_logger import GetLogger

# 获取log日志器
log = GetLogger().get_logger()


class PageLogin(Base):
    # 点击 登录链接
    def page_click_login_link(self):
        log.info("[page_loging] 执行:{} 点击链接操作".format(page.login_link))
        self.base_click(page.login_link)

    # 输入用户名
    def page_input_username(self, username):
        log.info("[page_loging] 对:{} 元素 输入用户名:{} 操作".format(page.login_username, username))
        self.base_input(page.login_username, username)

    # 输入密码
    def page_input_pwd(self, pwd):
        log.info("[page_loging] 对:{} 元素 输入密码:{} 操作".format(page.login_pwd, pwd))
        self.base_input(page.login_pwd, pwd)

    # 输入验证码
    def page_input_verify_code(self, verify_code):
        log.info("[page_loging] 对:{} 元素 输入验证码:{} 操作".format(page.login_verify_code, verify_code))
        self.base_input(page.login_verify_code, verify_code)

    # 点击登录按钮
    def page_click_login_btn(self):
        self.base_click(page.login_btn)

    # 获取 错误提示信息
    def page_get_error_info(self):
        return self.base_get_text(page.login_err_info)

    # 点击 错误提示框 确定按钮
    def page_click_error_alert(self):
        self.base_click(page.login_err_ok_btn)

    # 判断是否登录成功
    def page_if_login_success(self):
        # 注意 一定要将找元素的结果返回,True:存在
        return self.base_element_is_exist(page.login_logout_link)

    # 点击 安全退出
    def page_click_logout_link(self):
        self.base_click(page.login_logout_link)

    # 判断是否退出成功
    def page_if_logout_success(self):
        return self.base_element_is_exist(page.login_link)

    # 组合业务方法  -->登录业务直接调用
    def page_login(self, username, pwd, verify_code):
        log.info("[page_loging] 正在执行登录操作, 用户名:{} 密码:{}, 验证码:{}".format(username, pwd, verify_code))
        # 调用 输入用户名
        self.page_input_username(username)
        # 调用 输入密码
        self.page_input_pwd(pwd)
        # 调用 输入验证码
        self.page_input_verify_code(verify_code)
        # 调用 点击登录
        self.page_click_login_btn()

    # 组合登录业务方法 给(购物车模块、订单模块、支付模块)依赖登录使用
    def page_login_success(self, username="13812345678", pwd="123456", verify_code="8888"):
        # 点击登录连接
        self.page_click_login_link()
        log.info("[page_loging] 正在执行登录操作, 用户名:{} 密码:{}, 验证码:{}".format(username, pwd, verify_code))
        # 调用 输入用户名
        self.page_input_username(username)
        # 调用 输入密码
        self.page_input_pwd(pwd)
        # 调用 输入验证码
        self.page_input_verify_code(verify_code)
        # 调用 点击登录
        self.page_click_login_btn()

                        sripts/cases(业务层):导包调用page页面

                                实现

                                        1.模块:test+实际操作模块名称如:test_login.py

                                        2.测试业务名称:以大驼峰方法将模块名抄进来,有下划线去掉下划线

                                        3.方法

                                                1).初始化方法setup()注:在unittest框架中不能使用def__init_()方法

                                                        1.1).实例化页面对象

                                                        1.2).前置操作如:打开等等

                                                2).结束方法teardown

                                                        2.1).关闭驱动

                                                3).测试方法

                                                        3.1).根据要操作的业务实现

                                代码

import unittest

from base.get_driver import GetDriver
from page.page_login import PageLogin
from parameterized import parameterized
from tool.read_txt import read_txt
from base.get_logger import GetLogger

# 获取log日志器
log = GetLogger().get_logger()


def get_data():
    arrs = []
    for data in read_txt("login.txt"):
        arrs.append(tuple(data.strip().split(",")))
    return arrs[1:]


# 新建 登录测试类 并 继承 unittest.TestCase
class TestLogin(unittest.TestCase):
    # 新建 setupClass
    @classmethod
    def setUpClass(cls):
        try:
            # 实例化 并获取driver
            cls.driver = GetDriver().get_driver()
            # 实例化 PageLogin()
            cls.login = PageLogin(cls.driver)
            # 点击登录连接
            cls.login.page_click_login_link()
        except Exception as e:
            log.error("错误:{}".format(e))
            # 截图
            cls.login.base_get_image()

    # 新建 tearDownClass
    @classmethod
    def tearDownClass(cls):
        # 关闭drier驱动对象
        GetDriver().quit_driver()

    # 新建 登录测试方法
    @parameterized.expand(get_data())
    def test_login(self, username, pwd, verify_code, expect_result, status):
        try:
            # 调用 登录业务方法
            self.login.page_login(username, pwd, verify_code)
            # 判断是否为正向
            if status == "true":
                # 断言是否登录成功
                try:
                    self.assertTrue(self.login.page_if_login_success())
                except Exception as e:
                    # 截图
                    self.login.base_get_image()
                    log.error("错误:{}".format(e))
                    raise  # 主动抛出异常,否则HTMLTestRunner无法显示该错误
                # 点击 安全退出
                self.login.page_click_logout_link()
                # 点击登录连接
                self.login.page_click_login_link()
            # 逆向用例
            else:
                # 获取错误提示信息
                msg = self.login.page_get_error_info()
                print("msg:", msg)
                try:
                    self.assertEqual(msg, expect_result)
                except Exception as e:
                    # 截图
                    self.login.base_get_image()
                    log.error("错误:{}".format(e))
                    raise
                # 点击错误提示框 确定按钮
                self.login.page_click_error_alert()
        except Exception as e:
            log.error("错误:{}".format(e))
            # 截图
            self.login.base_get_image()
            raise

        V5:对PO分层之后的代码继续优化

        V6:PO模式深入封装,把共同操作提取封装到父类中,子类直接调用父类的方法                

二、今日学习思维导图

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

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

相关文章

redis面试(十六)公平锁释放和排队加锁

锁释放 RedissonFairLock.unlockInnerAsync()方法 这和加锁的逻辑没有太大区别 也就是说在客户端A他释放锁的时候,也会走while true的脚本逻辑,看一下有序集合中的元素的timeout时间如果小于了当前时间,就认为他的那个排队就过期了&#xf…

Spring自动注册-<bean>标签和属性解析

xml文件中最常见也最核心的就是<bean>,<Import>,<beans>,<alias>标签,关于它们的解析主要是BeanDefinitionParserDelegate类中.<bean>标签的解析最为复杂和重要. <bean>标签 processBeanDefinition(ele, delegate)方法中,主要是是对…

数据库管理-Redis

数据库管理-Redis 一、关系型数据库和非关系型数据库1、关系型数据库&#xff08;Relational Database Management System, RDBMS&#xff09;&#xff1a;2、非关系型数据库&#xff08;NoSQL Database Management System&#xff09;&#xff1a; 二、redis简述 redis是把数据…

苦WPS云盘已久矣

主要因为软件更新后&#xff0c;设置位置都会跑到其他地方 打开wps客户端后&#xff0c;点击电脑底部任务栏的云朵图标。 2. 找到存储位置后&#xff0c;点击“更换位置”。 来自https://www.wps.cn/mlearning/question/detail/id/333165.html

Java | Leetcode Java题解之第328题奇偶链表

题目&#xff1a; 题解&#xff1a; class Solution {public ListNode oddEvenList(ListNode head) {if (head null) {return head;}ListNode evenHead head.next;ListNode odd head, even evenHead;while (even ! null && even.next ! null) {odd.next even.nex…

编程学习笔记秘籍:开启高效学习之旅

引言&#xff1a; “计算机科学教育不能使人成为程序员&#xff0c;就像学画笔和颜料不能使人成为画家一样。”——埃里克雷蒙德。在当今数字化的时代&#xff0c;编程如同一把神奇的钥匙&#xff0c;能够打开无数机遇的大门。然而&#xff0c;编程知识的海洋广阔无垠&#xff…

正也科技:医药营销管理数字化建设的重要性及其迭代方向

第三届MAH合作与创新大会暨浙江省医药产业博览会于上周在杭州圆满结束。会议汇集了众多医药领域的专家、行业领袖和企业家&#xff0c;共同探讨医药行业的转型之路与实战经验。在中国医药新趋势分论坛&#xff0c;与会人员重点讨论了当前中国医药产业在新环境下所面临的挑战、变…

文心快码 Baidu Comate 前端工程师观点分享:行业现状(二)

本系列视频来自百度工程效能部的前端研发经理杨经纬&#xff0c;她在由开源中国主办的“AI编程革新研发效能”OSC源创会杭州站105期线下沙龙活动上&#xff0c;从一款文心快码&#xff08;Baidu Comate&#xff09;前端工程师的角度&#xff0c;分享了关于智能研发工具本身的研…

Hackademic.RTB1靶机复现

查看靶机的MAC地址 使用nmap进行扫描 使用dirsearch进行目录扫描 网站登录 点击紫色字体 进一步进行目录扫描 进行拼接 拼接wp-content 拼接wp-includes 点击Got root 测试发现不存在SQL注入 点击posted in Uncategorized 测试发现存在SQL注入 测试数据库 python sqlmap.py…

Linux-Shell三剑客grep,awk,sed-08

awk、grep、sed是linux操作文本的三大利器&#xff0c;合称文本三剑客&#xff0c;也是必须掌握的linux命令之一。三者的功能都是处理文本&#xff0c;但侧重点各不相同&#xff0c;其中属awk功能最强大&#xff0c;但也最复杂。grep更适合单纯的查找或匹配文本&#xff0c;sed…

UDP通信如何测试使用

用户数据报协议(User Datagram Protocol,UDP)是一种无连接的传输层通信协议,用于互联网应用程序。与TCP(传输控制协议)不同,UDP提供了较少的服务并且不保证数据的可靠传输,但它以更少的系统资源消耗和更低的延迟作为交换,这使得UDP非常适合那些对速度要求高而对数据丢…

【LLM之RAG】GraphRAG论文阅读笔记

研究背景 本文探讨了大型语言模型&#xff08;LLM&#xff09;如何通过从外部知识源检索相关信息来回答关于私有或之前未见过的文档集合的问题。特别指出&#xff0c;传统的检索增强生成&#xff08;RAG&#xff09;系统在处理全局问题时存在局限性&#xff0c;例如问整个文本…

四种应用层协议——MQTT、CoAP、WebSockets和HTTP——在工业物联网监控系统中的性能比较

目录 摘要(Abstract) 实验设置 实验结果 节选自《A Comparative Analysis of Application Layer Protocols within an Industrial Internet of Things Monitoring System》&#xff0c;作者是 Jurgen Aquilina、Peter Albert Xuereb、Emmanuel Francalanza、Jasmine Mallia …

java计算机毕设课设—CRM客户关系管理系统((附源码、文章、相关截图、部署视频)

这是什么系统&#xff1f; java计算机毕设课设—CRM客户关系管理系统&#xff08;(附源码、文章、相关截图、部署视频) CRM客户关系管理系统是一种基于Java开发的系统&#xff0c;旨在帮助企业更有效地管理与客户的互动。该系统通过优化客户信息的组织和分析&#xff0c;增强…

【Electron】npm安装Electron项目失败报错问题和解决办法

前言 闲来无事&#xff0c;便想着研究一下Electron&#xff0c;没想到安装直接就卡住了 问题 npm ERR! RequestError: Hostname/IP does not match certificates altnames: Host: npm.taobao.org. is not in the certs altnames: DNS:*.tbcdn.cn, DNS:*.taobao.com, DNS:*.al…

pygame小游戏

代码存在一些bug&#xff0c;感兴趣可自行修改&#xff0c;游戏运行后玩法与吃金币游戏类似。&#xff08;代码及结果比较粗糙&#xff0c;仅供参考&#xff09; 注&#xff1a;&#xff08;图片、音乐、音效文件老是上传上传不上&#xff0c;想要可私&#xff0c;也可以自己找…

CentOS 7 安装详细教程

​ 大家好&#xff0c;我是程序员小羊&#xff01; 前言&#xff1a; CentOS 7 是一个稳定的企业级 Linux 发行版&#xff0c;广泛用于服务器环境。CentOS 7 是基于 Red Hat Enterprise Linux (RHEL) 构建的企业级 Linux 发行版&#xff0c;提供免费的、开源的操作系统&#xf…

JVM-Java虚拟机-一文读懂-小白秒懂,老鸟进阶,必考面经

目录 JVM定义 Java Virtual Machine组成结构 各部分详解 0、类加载器 1、栈是运行的单位&#xff0c;堆是存储的单位 2、栈中存放各个栈帧 3、方法区——多线程共享区域 JVM定义 运行Java程序的平台内存结构、 垃圾回收、 类加载、 性能调优、 JVM 自身优化技术、 执行引…

【Web前端】vue3整合eslint约束代码格式

一、整合eslint 整合eslint的两种方式&#xff1a; 在已有项目中整合eslint&#xff1a;# 安装eslint及其vue插件即可 npm i -D eslint eslint-plugin-vue创建项目时整合eslint&#xff1a; 提示 是否引入ESLint用于代码质量检测 时选择 是# 创建vue3项目 npx create-vue # 下…

sqlserver同一数据库还原实例冲突可以用[文件和文件组]来备份解决

1.将数据库恢复模式改为完整 2.备份 在数据库右键--任务--备份 选择文件和文件组 选择位置备份 3.还原 在数据库上右键--还原文件和文件组 或者还原时修改实例名称&#xff08;没试过&#xff09;