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 ...
随机推荐
- 选择最佳策略:简单的方式和globals()
promos = [fidelity_promo, bulk_item_promo, large_order_promo] ➊ def best_promo(order): ➋ "" ...
- 3.解决git不可用问题
升级gityum -y update git 配置阿里云yum源yum -y update nssyum -y update nss curl libcurl
- Ansible 自动化工具入门
Ansible 配置 Ansible 简单管理 下面的小实验,我们将采用以下的IP地址规划,请自行配置好. [实验环境] [状态] [主机列表] [python版本] 控制机 192.168.1.10 ...
- SMTP实现发送邮箱1
#include "stdafx.h" #include <iostream> #include <WinSock2.h> using namespace ...
- ListUtils的简单集合操作和原理
1 Maven依赖 <dependency> <groupId>org.apache.commons</groupId> <artifactId>com ...
- Image 对象事件
以前没怎么注意image上的事件 Image 对象事件 事件 描述 W3C onabort 当用户放弃图像的装载时调用的事件句柄. Yes onerror 在装载图像的过程中发生错误时调用的事件句柄. ...
- Web框架概述——React.js
目前,在前端Web开发中,三大热门框架为React.js,Vue.js,Angular.js .当然,三大框架各有各的优缺点,这里就不多说了,下面我就针对前段时间所学的React框架做一下整体知识点的 ...
- 销售订单(SO)-API-更新销售订单
更新销售订单和创建销售订单差不多,调用的API相同,只是传入的时候标识不一样:operation := oe_globals.g_opr_update 示例代码如下: PROCEDURE update ...
- 在vue中引用echarts导致Cannot read property getAttribute of null ?
报错信息如下: 之前一直用echarts没有出现过这个问题,所以当这个问题出现时我就开始了各种查,试了几种方法依旧报错,比如: 1.在mounted() {},写成如下形式:(依旧报错) this.$ ...
- python生成式:列表、字典、集合
python的3类生成式: 列表生成式 字典生成式 集合生成式 1.python列表生成式 my_data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] print(&quo ...