Selenium page object模式Python

目录

概述

优点

示例

项目结构:

基础页面类BasePage

业务页面类BaiduHomePage

测试类test_baidu:

文件工具类file_util

运行日志:

测试结果:


概述

在web应用程序的UI中,有一些区域可以与测试交互。页面对象仅将这些对象建模为测试代码中的对象。这减少了重复代码的数量,意味着如果UI发生更改,则只需在一个位置应用修复。

页面对象是一种在测试自动化中流行的设计模式,用于增强测试维护和减少代码重复。页面对象是一个面向对象的类,用作AUT页面的接口。然后,每当需要与该页面的UI交互时,测试就会使用该页面对象类的方法。好处是,如果页面的UI发生了更改,则测试本身不需要更改,只需要更改页面对象中的代码。随后,所有支持新UI的更改都位于同一位置。

优点

测试代码和特定于页面的代码之间有一个清晰的分离,例如定位器(或者如果您使用的是UI映射,则使用它们)和布局。

  • 页面提供的服务或操作只有一个存储库,而不是将这些服务分散在整个测试中。
  • 在这两种情况下,这都允许在一个地方进行由于UI更改而需要的任何修改。

示例

演示一个百度搜索功能搜索关键字selenium并做断言,然后保存断言结果截图到对应日期文件夹。

项目结构:

基础页面类BasePage

封装页面基本操作,主要是跟业务不相关的公共方法,如:元素查找、点击、文本输入、截图、双击、获取元素坐标等,跟页面相关的方法不建议封装在基础页面类中,以下是一个简单的实例:

import logging
import time
import traceback

from selenium.webdriver import ActionChains
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
import selenium.webdriver.support.expected_conditions as EC
import os
from datetime import datetime


class Action:
    def __init__(self, dirver):
        self.driver = dirver

    def find_element(self, loc):
        try:
            # WebDriverWait(self.driver, 10).until(EC.visibility_of_all_elements_located(loc))
            WebDriverWait(self.driver, 15).until(lambda x: x.find_element(*loc).is_displayed())

            element = self.driver.find_element(*loc)
            self.get_element_coordinate(element)
        except Exception as e:
            traceback.print_exception(e)
            traceback.print_stack()
            logging.info("页面中没有%s %" % (self.loc[1]))
        else:
            return element

    def find_elements(self, loc):
        try:
            # 保证元素可见
            WebDriverWait(self.driver, 15).until(EC.visibility_of_all_elements_located(loc))

            elements = self.driver.find_elements(*loc)
        except Exception as e:
            traceback.print_exception(e)
            traceback.print_stack()
            logging.info("页面中没有%s %" % (self.loc[1]))
        else:
            return elements

    def get_element_coordinate(self, element):
        rect = element.rect
        x, y = rect['x'], rect['y']
        size = element.size
        width = size['width']
        height = size['height']
        left_up = (x, y)
        left_down = (x, y + height)
        center = (x + 0.5 * width, y + 0.5 * height)
        right_up = (x + width, y)
        right_down = (x + width, y + height)
        element_coordinate = dict(left_up=left_up, left_down=left_down, center=center, right_up=right_up,
                                  right_down=right_down)
        logging.info(element_coordinate)
        print(element_coordinate)
        return element_coordinate

    def click_element(self, loc):
        self.find_element(loc).click()

    def save_element_picture(self, loc, file_name):
        element = self.find_element(loc)
        element.screenshot(file_name)

    def save_picture(self, filename, browser='FireFox'):
        file_path = os.path.dirname(os.path.dirname(__file__))
        now = datetime.now()

        picture_date_dir = now.strftime("%Y-%m-%d")
        formatted_date = now.strftime("%Y-%m-%d-%H-%M-%S")
        file_path = file_path + '\\picture\\' + picture_date_dir
        picture_path = file_path + '\\' + filename + '_' + formatted_date + '.png'
        print(picture_path)
        # 检查文件路径是否存在
        if not os.path.exists(file_path):
            print('创建文件路径')
            # 如果文件路径不存在,创建它
            os.makedirs(file_path)
        if browser == 'FireFox':

            self.driver.save_full_page_screenshot(picture_path)
        elif browser == 'Chrome':
            self.driver.get_screenshot_as_file(picture_path)

    def save_long_picture(self, filename, browser='FireFox'):
        # file_path=

        # 截长图
        self.driver.save_full_page_screenshot(filename)

    def click_element_with_coordinate(self, x, y):
        action = ActionBuilder(self.driver)
        action.pointer_action.move_to_location(x, y).click()
        action.perform()

    def double_click(self, loc):
        element = self.find_element(loc)
        ActionChains(self.driver).double_click(element).perform()

    
   
    def set_high_light_elment(self, element):
        script = '''
                // 高亮显示元素
        arguments[0].style.backgroundColor = "yellow";

        // 设置红色边框
        arguments[0].style.border = "3px solid red";
        '''
        self.driver.execute_script(script, element)

        pass

    def browser_2_windows_coordinates_v2(self, browserX, browserY, screenWidth=1360, screenHeight=768,
                                         desktopScale=1):
        # location = self.get_element_location(element)
        # x, y = location['left_up'][0], location['left_up'][1]
        script = '''
        function getDesktopCoordinates(browserX, browserY,screenWidth,screenHeight,desktopScale) {{
        // 浏览器中的坐标(x, y)
        var browserX = browserX;
        var browserY = browserY;
        // 屏幕分辨率
        var screenWidth = screenWidth;
        var screenHeight = screenHeight;
        // 桌面缩放比例
        var desktopScale = desktopScale;
        //- 浏览器窗口左上角的桌面坐标为(win_x, win_y)。
        var win_x = window.screenX || window.screenLeft;
        var win_y = window.screenY || window.screenTop;
        //计算工具栏高度
        var toolbarHeight = window.outerHeight - window.innerHeight;
        // 计算桌面坐标
        var desktopX =(win_x+ browserX) * (screenWidth/window.innerWidth) ;
        var desktopY =(win_y+ browserY+toolbarHeight) * (screenHeight/ window.innerHeight );
        console.log("桌面坐标 (x, y):", desktopX, desktopY);
        // 创建包含坐标的对象
        var desktopCoordinates = {{
        desktopX: desktopX,
        desktopY: desktopY
        }};
        return desktopCoordinates;
        }}
        var coordinates = getDesktopCoordinates({browserX}, {browserY},{screenWidth},{screenHeight},{desktopScale});
        return coordinates;
        '''.format(browserX=browserX, browserY=browserY, screenWidth=screenWidth, screenHeight=screenHeight,
                   desktopScale=desktopScale)
        logging.info(script)
        desktopCoordinates = self.driver.execute_script(script)
        logging.info(desktopCoordinates)
        return desktopCoordinates

    def mark_dom(self, x, y, color='red'):
        script = '''
        // 创建黑点DOM
        const dot = document.createElement('div');
        dot.style.position = 'absolute';
        dot.style.width = '10px';
        dot.style.height = '10px';
        dot.style.backgroundColor = '{}';
        dot.style.borderRadius = '50%';
        dot.style.left = {} + 'px';
        dot.style.top = {} + 'px';
        document.body.appendChild(dot);
        '''.format(color, round(x, 0), round(y, 0))
        logging.info(script)
        print(script)

        self.driver.execute_script(script)

    def mark_dom_text(self, text, x, y):
        script = '''
        // 创建一个新的标记元素
        var newElement = document.createElement("span");
        // 设置标记元素的文本内容
        newElement.innerText = "{}";
        // 设置标记元素的位置样式
        newElement.style.position = "absolute";
        newElement.style.left = "{}px";
        newElement.style.top = "{}px";
        newElement.style.color = "red";
        // 将新的标记元素附加到目标元素中
        document.body.appendChild(newElement);
        '''.format(text, round(x, 0), round(y, 0))

        logging.info(script)
        self.driver.execute_script(script)

业务页面类BaiduHomePage

该类主要是描述待测页面中的元素及对应的操作,当页面元素发生变更时可以快速在该页面进行修改降低了业务代码和测试代码的耦合性

import time

import pyautogui as pyautogui
from selenium.webdriver.common.by import By

from PythonPractise.selenium_po.page import BasePage


class BaiduHomePage(BasePage.Action):
    search_input_loc = (By.ID, '''kw''')
    search_btn_loc = (By.ID, '''su''')

    def search_text(self):
        search_input=self.find_element(self.search_input_loc)
        self.set_high_light_elment(search_input)
        location=self.get_element_coordinate(search_input)
        browser_x,browser_y=location['center'][0],location['center'][1]
        self.mark_dom(browser_x,browser_y)
        desktopCoordinates=self.browser_2_windows_coordinates_v2(browser_x,browser_y)
        x,y=desktopCoordinates['desktopX'],desktopCoordinates['desktopY']
        # 移动到拖拽元素中心坐标
        pyautogui.moveTo(x, y, duration=1, tween=pyautogui.linear)
        time.sleep(10)
        self.find_element(self.search_input_loc).send_keys('Selenium')
        self.click_element(self.search_btn_loc)
        self.save_picture('selenium')

测试类test_baidu:

测试方法类,主要描述对页面逻辑测试的验证,在此方法中会初始话待测页面类,如本例中的百度首页的测试,本类主要关注业务逻辑的操作及验证,引入页面对象降低测试代码和业务代码的耦合性增强了代码的可读性,也降低了和页面代码的耦合性。

该方法首先打开百度首页,然后搜索对应的关键字并做校验,方法结束后关闭测试会话。

import time

from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.firefox.service import Service as FirefoxService
from webdriver_manager.firefox import GeckoDriverManager

from PythonPractise.selenium_po.page import BaiduHomePage
from selenium import webdriver


class TestCase:

    def setup_class(self):
        # self.driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()))
        # self.driver = webdriver.Chrome(executable_path='F:\\PycharmProjects\\PythonPractise\\selenium_po\\driver\\chromedriver.exe')
        self.driver= webdriver.Firefox(service=FirefoxService(GeckoDriverManager().install()))
        self.driver.get("http://www.baidu.com")

    def test_baidu_search(self):
        baidu_home_page = BaiduHomePage.BaiduHomePage(self.driver)
        baidu_home_page.search_text()
        time.sleep(5)
        baidu_home_page.save_picture('selenium')
        assert 'selenium' in self.driver.page_source

    def teardown_class(self):
        self.driver.quit()

文件工具类file_util

此方法主要是定义项目的文件位置方便后面的截图文件、日志文件等其他测试过程中产生的文件的保存,大家根据实际需求进行扩展。

import os
from datetime import datetime

# 获取当前日期和时间
now = datetime.now()

# 格式化为 yyyy-mm-dd
formatted_date = now.strftime("%Y-%m-%d :%H:%M:%S")

print(formatted_date)
project_path = os.path.dirname(os.path.dirname(__file__))
print(project_path)

运行日志:

E:\Python3.11\python.exe "E:/PyCharm Community Edition 2023.1.1/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path F:\PycharmProjects\PythonPractise\selenium_po\testcase\test_baidu.py 
Testing started at 15:42 ...
Launching pytest with arguments F:\PycharmProjects\PythonPractise\selenium_po\testcase\test_baidu.py --no-header --no-summary -q in F:\PycharmProjects\PythonPractise\selenium_po\testcase

============================= test session starts =============================
collecting ... collected 1 item

test_baidu.py::TestCase::test_baidu_search 

============================= 1 passed in 42.88s ==============================
PASSED                        [100%]{'left_up': (353.0, 188.3999938964844), 'left_down': (353.0, 232.3999938964844), 'center': (628.0, 210.3999938964844), 'right_up': (903.0, 188.3999938964844), 'right_down': (903.0, 232.3999938964844)}
{'left_up': (353.0, 188.3999938964844), 'left_down': (353.0, 234.3999938964844), 'center': (629.0, 211.3999938964844), 'right_up': (905.0, 188.3999938964844), 'right_down': (905.0, 234.3999938964844)}

        // 创建黑点DOM
        const dot = document.createElement('div');
        dot.style.position = 'absolute';
        dot.style.width = '10px';
        dot.style.height = '10px';
        dot.style.backgroundColor = 'red';
        dot.style.borderRadius = '50%';
        dot.style.left = 629.0 + 'px';
        dot.style.top = 211.0 + 'px';
        document.body.appendChild(dot);
        
{'left_up': (353.0, 188.3999938964844), 'left_down': (353.0, 234.3999938964844), 'center': (629.0, 211.3999938964844), 'right_up': (905.0, 188.3999938964844), 'right_down': (905.0, 234.3999938964844)}
{'left_up': (725.0, 15.0), 'left_down': (725.0, 55.0), 'center': (781.0, 35.0), 'right_up': (837.0, 15.0), 'right_down': (837.0, 55.0)}
F:\PycharmProjects\PythonPractise\selenium_po\picture\2023-12-02\selenium_2023-12-02-15-42-56.png
F:\PycharmProjects\PythonPractise\selenium_po\picture\2023-12-02\selenium_2023-12-02-15-43-01.png

进程已结束,退出代码0

测试结果:

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

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

相关文章

[数据结构]红黑树的定义以及添加原则

红黑树是一种自平衡的二叉查找树,是一种常用的数据结构 1972年出现,在当时被称为平衡二叉B树。后来1978年被修改为如今的“红黑树” 它是一个特殊的二叉查找树,红黑树的每一个节点上都有储存位表示节点的颜色 每一个节点可以是红或者黑&#…

KNN实战-图像识别

数据说明 是在循环0-9的数字一直循环500次所得到的数据,然后以手写照片的形式存在 识别的步骤 加载数据构建目标值构建模型参数调优可视化展示 加载数据 import numpy as np import matplotlib.pyplot as plt # 记载数据 data np.load(./digit.npy) data构建目…

什么是Ros(二)- 结构和通讯概述

目录 1.架构 2.通讯 参考文献 上接:什么是Ros(一)-CSDN博客 1.架构 共三层:OS 层,中间层,应用层。 OS 层:OS 层是操作系统层也就是我们现在使用的ubuntu(linux)&…

【Java Web学习笔记】 2 - CSS入门

项目代码 零、 CSS引出 CSS 教程 官方教学文档 1.在没有CSS之前,我们想要修改HTML元素的样式需要为每个HTML元素单独定义样式属性,费心费力。所以CSS就出现了。 2.使用CSS将HTML页面的内容与样式分离提高web开发的工作效率(针对前端开发&a…

Spring Cloud笔记 —— 什么是Spring Cloud?

引言: 在写这篇博客之前,其实吧,博主很久之前有过一段时间的Spring Cloud的案例项目开发经验,就是一个案例项目开发而已,也说不上有多高大上,那个时候,我其实也是从众而已罢了,毕竟现…

Java 设计模式系列:代理模式

文章目录 介绍静态代理基本介绍应用实例静态代理优缺点 动态代理基本介绍JDK 中生成代理对象的 API Cglib 代理基本介绍实现步骤 介绍 1)代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象 2&#xff09…

JOSEF 快速中间继电器 KZJ-4H-L DC220V 导轨安装

快速中间继电器KZJ-4H-LDC220V导轨安装导轨安装是广泛用于电力系统,能够断货开或开通大负载,并且具有较强的断弧能力,适用于交流50/60Hz。电压24380V,直流电压24280V自动控制电路中以增加保护和控制回路的触点数量与触点容量。 KZJ系列快速中…

leetcode 209. 长度最小的子数组(优质解法)

代码&#xff1a; //时间复杂度 O(N) ,空间复杂度 O(1) class Solution {//采用滑动窗口的方法解决public int minSubArrayLen(int target, int[] nums) {int numsLengthnums.length;int minLengthInteger.MAX_VALUE;int left0;int right0;int sum0;while (right<numsLengt…

详解原生Spring框架下的方法切入点表达式

&#x1f609;&#x1f609; 学习交流群&#xff1a; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 &#x1f96d;&#x1f96d;3&#xff1a;QQ群&#xff1a;583783…

C++/Qt读写xml文件

今天介绍C/Qt如何读写xml文件&#xff0c;xml文件一般用于作为配置文件使用。 C C读写xml文件需要借助第三方来实现&#xff0c;比较好用的有tinyxml2和pugixml&#xff0c;对应的网址链接。 tinyxml2 pugixml 以tinyxml2为例&#xff0c;下载后进行解压可以看到以下文件&…

Python---格式化输出与%百分号----涉及转义符 \ 反斜杠的使用

相关链接Python--格式化输出中的转义符号----\t 制表符&#xff08;空格的&#xff09;和\n&#xff08;换行的&#xff09;_唯元素的博客-CSDN博客 Python---字符串&#xff08;用单、双引号、 三单/双引号定义。反斜杠 \ 转义&#xff0c;单在双内/双在单内 &#xff09;-CS…

【电路笔记】-串联和并联电阻

串联和并联电阻 文章目录 串联和并联电阻1、概述2、串联和并联电阻示例13、串联和并联电阻示例2 电阻器可以无限数量的串联和并联组合连接在一起&#xff0c;形成复杂的电阻电路。 1、概述 在之前的教程中&#xff0c;我们学习了如何将各个电阻器连接在一起以形成串联电阻器网…

混沌系统在图像加密中的应用(基于哈密顿能量函数的混沌系统构造1.5)

混沌系统在图像加密中的应用&#xff08;基于哈密顿能量函数的混沌系统构造1.5&#xff09; 前言一、自治非哈密顿系统的构造、动态特性分析1.相关理论基础2.两个四维自治非哈密顿系统3.数值分析 python代码 前言 续接混沌系统在图像加密中的应用&#xff08;基于哈密顿能量函…

重生奇迹MU再生原石

通过坎特鲁提炼之塔的NPC艾尔菲丝提炼成功就可以可获得再生宝石。 重生奇迹mu里的再生原石的用法&#xff1a; 1、打怪获得再生原石去提炼之塔&#xff08;进入坎特鲁遗址的141188位置的传送台&#xff09;。 2、找到&#xff08;艾儿菲丝&#xff09;把原石提炼成再生宝石。…

MySQL安全相关——TDE和数据脱敏功能介绍

MySQL作为一款广泛使用的开源关系型数据库管理系统(RDBMS)&#xff0c;其安全性一直是开发者和企业关注的重点。在MySQL中&#xff0c;有一些与安全相关的功能&#xff0c;其中包括Transparent Data Encryption(TDE)和数据脱敏。本文将对这些功能进行介绍。 一、Transparent Da…

深度学习实战63-利用自适应混合金字塔网络实现人脸皮肤美颜效果,快速部署与实现一键美颜功能

大家好,我是微学AI,今天给大家介绍一下深度学习实战63-利用自适应混合金字塔网络实现人脸皮肤美颜效果,快速部署与实现一键美颜功能。在本文中,我将介绍一种新颖的自适应混合金字塔网络(ABPN),该网络可以实现对超高分辨率照片的快速局部修饰。该网络主要由两个组件组成:一…

最简单的梅花吉凶表

以下是梅花易数吉凶表&#xff0c;使用方式&#xff1a; 随机报2组数字&#xff1a;第1个数除8得余数作为上爻&#xff0c;第2个数除8得余数作为下爻&#xff0c;然后对照以下表格&#xff0c;得到吉凶预测结果。 说明&#xff1a;经过个人不断实践&#xff0c;[大吉转大吉] …

基于STC12C5A60S2系列1T 8051单片机的液晶显示器LCD1602显示功能菜单应用

基于STC12C5A60S2系列1T 8051单片机的液晶显示器LCD1602显示功能菜单应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍液晶显示器LCD1602简单介绍IIC通信简单介绍掉…

Android : SQLite 增删改查—简单应用

示例图&#xff1a; 学生实体类 Student.java package com.example.mysqlite.dto;public class Student {public Long id;public String name;public String sex;public int age;public String clazz;public String creatDate;//头像public byte[] logoHead;Overridepublic St…

分享一个判断曲线的趋势的Demo

需求背景 最近在处理数据&#xff0c;横坐标是时间&#xff0c;纵坐标是价格&#xff0c;需要判断一段时间内&#xff0c;由这些点绘制成的曲线的走势&#xff0c;比如趋势朝上&#xff0c;趋势朝下&#xff0c;水平调整这三种趋势。尝试了不少方法&#xff0c;下面这个效果还…