Python PageFactory-使用配置文件动态生成页面PageObject
需求
在Python Selenium 的 PageObject模式中,一般每个页面需要写一个类, 一种PageObject的写法如下:
class BaiduPageObject(object):
def __init__(self, driver): # 一般通过继成BasePage类实现该方法
self.driver = driver
# 页面元素 -----
search_ipt_loc = ('id', 'kw')
search_btn_loc = ('id', 'su')
# 页面操作 -----
def input_search_ipt(self, keyword):
search_ipt = self.driver.find_element(*search_ipt_loc)
search_ipt.clear()
search_ipt.send_keys(keyword):
def click_search_btn(self):
self.driver.find_element(*search_btn_loc).click()
def search(self, keyword):
self.input_search_ipt(keyword)
self.click_search_btn()
对于一个成型的UI项目来说, 在编写用例前, 动辄要先编写数十上百个这种PageObject类. 鉴于每个PageObject类的格式相似, 可以采用
Page Factory(页面工厂)+配置文件(yaml) -> PageObject
的方式来动态生成每一个页面对象
PageFactory本是Selenium Java SDK中的一种基于PageObject的设计模式, 使用
@FindBy(id = "kw")
语法糖来标记页面元素, 并支持使用@CacheLookup
来缓存页面元素(这样元素只需要查找一次)
完整介绍: https://github.com/SeleniumHQ/selenium/wiki/PageFactory
鉴于 Python的动态特性, 这里的Page Factory的实现方式与Selenium Java SDK中的完全不同
设计与实现
首先设计及约定页面配置文件的格式如下:
# filename: baidu.yaml
url: https://www.baidu.com # 可选字段, 用于打开网页
title: 百度一下,你就知道 # 可选字段, 用于判断是否在当前网页
elements: # 页面元素
search_ipt: [id, kw] # 支持id, name, class name, tag name, link text
search_btn: [id, su] # partial link text, xpath, css selector
actions: # 可选字段, 页面操作
search:
input: [search_ipt, 默认值] # input: 固定字段, 第一参数为上面的元素, 第二个为输入的文本
click: search_btn # click: 固定字段, 参数为上面配置的元素
page_factory.py
# page_factory.py
from types import MethodType # 用于为实例动态绑定方法
from collections import OrderedDict # 有序字典, 用于按顺序遍历actions中的操作步骤
import yaml
from selenium import webdriver
class Page(object):
def __init__(self, driver, from_yaml=None, **kwargs):
self.driver = driver
self.url = kwargs.get('url', None)
self.title = kwargs.get('title', None)
self.elements = kwargs.get('elements', {})
self.actions = kwargs.get('url', OrderedDict())
if from_yaml is not None:
self.init_page(from_yaml)
def init_page(self, from_yaml):
with open(from_yaml, encoding='utf-8') as f:
data = yaml.load(f)
self.url = data.get('url', None)
self.title = data.get('title', None)
self.elements = data.get('elements', {})
self.actions = OrderedDict(data.get('actions', {}))
def open(self): # 打开页面url
if self.url is not None:
self.driver.get(self.url)
def on_page(self): # 通过title判断是否在当前页面上
if self.title is not None:
return self.title in self.driver.title
def input(self, element, text): # 输入操作, 指定配置中的元素名及文本
element = self.__getattr__(element) #
element.clear()
element.send_keys(text)
def click(self, element): # 点击操作, 指定元素名
element = self.__getattr__(element)
element.click()
def _handle_element(self, item): # 处理要查找的页面元素
element_loc = self.elements.get(item, None)
if element_loc is not None:
element = self.driver.find_element(*element_loc) # todo try
setattr(Page, item, element) # 缓存元素作为页面属性
return element
def _handle_action(self, item): # 处理要查找的操作方法
action = self.actions.get(item, None)
if action is not None:
def pack_action(self, text=None): # 解析约定的action格式, 组装操作方法
for step, params in action.items():
if step == 'input':
if text is not None and isinstance(params, list) and len(params)>1:
params[1] = text # 使用调用时的text参数替换input: [search_ipt, $keyword]的第2个参数
self.input(*params)
elif step == 'click':
self.click(params)
return MethodType(pack_action, self) # 为实例绑定操作方法并返回
def __getattr__(self, item): # 查找页面属性和操作方法
if not hasattr(Page, item): # 如无该属性/方法则动态为页面添加元素属性和操作方法
return self._handle_element(item) or self._handle_action(item)
if __name__ == '__main__': # 使用方法
driver = webdriver.Chrome()
baidu = Page(driver, 'baidu.yaml')
baidu.open()
baidu.search('临渊')
TODO
- 异常捕获
- 单url框架页面的加载, 每个action的返回页面, 页面之间的流转
- 缓存页面对象, 比如不同用例中多次调用baidu = PageFactory(driver, ...), 使用同一个退休
- 增加更多的action支持, 如select, hover, switch等等
- 独立模板解析parser方法, 支持更多的变量, 如
$1, $2/$keyword, $func(a)
等 - 为Page增加hooks方法让使用更灵活
PS:
使用该Page Factory(页面工厂)方法暂时还没有减轻, 数十上百的页面编写工作, 只是从原来的编写Python类改为编写yaml配置文件, 然而,静态的配置文件相对与Python类来说, 有以下好处:
- 实现了数据(页面元素定位器)与代码的分离
- PageFactory支持传入字典格式的配置参数来生成页面, 数据可以来源于yaml文件,也可以来源于页面表单/数据库,甚至爬虫爬取, 便于集成前端web页面, 或爬虫自动生成Pages
Python PageFactory-使用配置文件动态生成页面PageObject的更多相关文章
- 动态生成页面(一)——ASP.NET中Literal使用
在页面中加入内容时,假设是静态内容.无需使用容器,能够直接将标记作为HTML直接加入到页面中:可是,假设是动态内容,则必须借助容器将内容加入到页面中.典型的容器有:Label控件.Literal控件. ...
- python解析git log后生成页面显示git更新日志信息
使用git log可以查到git上项目的更新日志. 如下两个git项目,我想把git的日志信息解析成一个便于在浏览器上查看的页面. https://github.com/gityf/lua https ...
- sitemap.xml 静态和动态生成页面 shopnc二次开发 动态生成sitemap.xml
Sitemap 可方便网站管理员通知搜索引擎他们网站上有哪些可供抓取的网页.最简单的 Sitemap 形式,就是XML 文件,在其中列出网站中的网址以及关于每个网址的其他元数据(上次更新的时间.更改的 ...
- 通过js动态生成页面表格
var redlineTemplateP = $(".redlineDataList"); for (var index in detailArraryLists.rows){ v ...
- Javascript动态生成的页面信息爬取和openpyxl包FAQ小记
最近,笔者在使用Requests模拟浏览器发送Post请求时,发现程序返回的html与浏览器F12观察到的略有不同,经过观察返回的response.text,cookies确认有效,因为我们可以看到返 ...
- CGI实现页面的动态生成
传统的Web应用开发局限于有限的静态页面(HTML静态页面),不利于系统的扩展,不能提供及时信息,而且修改维护麻烦,所以建立一个动态Web应用程序尤为重要.一方面根据访问者的不同请求返回不同的访问信息 ...
- Python调用ansible API系列(四)动态生成hosts文件
方法一:通过最原始的操作文件的方式 #!/usr/bin/env python # -*- coding: utf-8 -*- """ 通过操作文件形式动态生成ansib ...
- Python+Selenium爬取动态加载页面(1)
注: 最近有一小任务,需要收集水质和水雨信息,找了两个网站:国家地表水水质自动监测实时数据发布系统和全国水雨情网.由于这两个网站的数据都是动态加载出来的,所以我用了Selenium来完成我的数据获取. ...
- SSI注入--嵌入HTML页面中的指令,类似jsp、asp对现有HTML页面增加动态生成内容,见后面例子
SSI注入漏洞总结 from:https://www.mi1k7ea.com/2019/09/28/SSI%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E6%80%BB%E ...
随机推荐
- vue页面顺序规范
// html模板<template> <div>因联vue页面规范</div></template><script> // 模块 ...
- 数据结构和算法总结(三):A* 寻路算法
前言 复习下寻路相关的东西,而且A star寻路在游戏开发中应用挺多的,故记录下. 正文 迪杰斯特拉算法 说起A*得先谈谈Dijkstra算法,它是在BFS基础上的一种带权值的两点最短寻路贪心算法. ...
- Tomcat的架构
Tomcat 7.0---Servlet API 3.0---JSP API 2.2---JDK 1.6 一个Tomcat实例,或者服务器(server),是Tomcat容器层次结构中的顶级组件. 只 ...
- sublime 配置大全
最近玩 python ,一般用的编译器是 pycharm ,功能强大,但是苦于启动速度遂准备换坑,瞄上了 sublime .这里记录一下 sublime 的设置以及坑爹项,需要注意的是我用的是 sub ...
- Introduction to Deep Learning Algorithms
Introduction to Deep Learning Algorithms See the following article for a recent survey of deep learn ...
- javaIO——BufferedWriter
[环境] jdk1.8 前面学习过 BufferedReader,是缓冲字符输入流.那么今天来学习对应的缓冲字符输出流类:BufferedWriter.跟 BufferedReader 同理,它也是一 ...
- css优先级及其对应的权重
1.选择器的优先级 !important>内联选择器(style)>id选择器>类选择器 | 属性选择器 | 伪类选择器 > 元素选择器>通配符(*) 2.选择器的权重( ...
- Java虚拟机内存基础、垃圾收集算法及JVM优化
1 JVM 简单结构图 1.1 类加载子系统与方法区 类加载子系统负责从文件系统或者网络中加载 Class 信息,加载的类信息存放于一块称 为方法区的内存空间.除了类的信息外,方法区中可能还会存放 ...
- 【51nod2026】Gcd and Lcm(杜教筛)
题目传送门:51nod 我们可以先观察一下这个$f(x)=\sum_{d|x}\mu(d) \cdot d$. 首先它是个积性函数,并且$f(p^k)=1-p \ (k>0)$,这说明函数$f( ...
- What is libacl.so.1 ?
Google says, "This package contains the libacl.so dynamic library which contains the POSIX 1003 ...