需求

在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类来说, 有以下好处:

  1. 实现了数据(页面元素定位器)与代码的分离
  2. PageFactory支持传入字典格式的配置参数来生成页面, 数据可以来源于yaml文件,也可以来源于页面表单/数据库,甚至爬虫爬取, 便于集成前端web页面, 或爬虫自动生成Pages

Python PageFactory-使用配置文件动态生成页面PageObject的更多相关文章

  1. 动态生成页面(一)——ASP.NET中Literal使用

    在页面中加入内容时,假设是静态内容.无需使用容器,能够直接将标记作为HTML直接加入到页面中:可是,假设是动态内容,则必须借助容器将内容加入到页面中.典型的容器有:Label控件.Literal控件. ...

  2. python解析git log后生成页面显示git更新日志信息

    使用git log可以查到git上项目的更新日志. 如下两个git项目,我想把git的日志信息解析成一个便于在浏览器上查看的页面. https://github.com/gityf/lua https ...

  3. sitemap.xml 静态和动态生成页面 shopnc二次开发 动态生成sitemap.xml

    Sitemap 可方便网站管理员通知搜索引擎他们网站上有哪些可供抓取的网页.最简单的 Sitemap 形式,就是XML 文件,在其中列出网站中的网址以及关于每个网址的其他元数据(上次更新的时间.更改的 ...

  4. 通过js动态生成页面表格

    var redlineTemplateP = $(".redlineDataList"); for (var index in detailArraryLists.rows){ v ...

  5. Javascript动态生成的页面信息爬取和openpyxl包FAQ小记

    最近,笔者在使用Requests模拟浏览器发送Post请求时,发现程序返回的html与浏览器F12观察到的略有不同,经过观察返回的response.text,cookies确认有效,因为我们可以看到返 ...

  6. CGI实现页面的动态生成

    传统的Web应用开发局限于有限的静态页面(HTML静态页面),不利于系统的扩展,不能提供及时信息,而且修改维护麻烦,所以建立一个动态Web应用程序尤为重要.一方面根据访问者的不同请求返回不同的访问信息 ...

  7. Python调用ansible API系列(四)动态生成hosts文件

    方法一:通过最原始的操作文件的方式 #!/usr/bin/env python # -*- coding: utf-8 -*- """ 通过操作文件形式动态生成ansib ...

  8. Python+Selenium爬取动态加载页面(1)

    注: 最近有一小任务,需要收集水质和水雨信息,找了两个网站:国家地表水水质自动监测实时数据发布系统和全国水雨情网.由于这两个网站的数据都是动态加载出来的,所以我用了Selenium来完成我的数据获取. ...

  9. 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 ...

随机推荐

  1. [转载]torch参数:torch.backends.cudnn.benchmark的意义

    [转载]torch参数:torch.backends.cudnn.benchmark的意义 来源:https://zhuanlan.zhihu.com/p/73711222 完整版请看原文,这里只截取 ...

  2. 使用Vue CLI脚手架搭建vue项目

    本次是使用@vue/cli 3.11.0版本搭建的vue项目 1. 首先确保自己的电脑上的Node.js的版本是8.9版本或者以上 2. 全局安装vue/cli npm install @vue/cl ...

  3. Apache httpclient的execute方法调试

    因为工作需要,想研究一下execute执行的逻辑. 在这一行调用execute: response = getHttpClient().execute(get); getHttpClient的实现: ...

  4. 【Java并发】线程通信

    一.概述 1.1 什么是多线程之间通讯? 1.2 案例 代码实现 解决线程安全问题 二.等待通知机制 2.1 示例 2.2 wait与sleep区别 三.Lock锁 3.1 概述 3.2 等待/通知机 ...

  5. sklearn--回归

    一.线性回归 LinearRegression类就是我们平时所说的普通线性回归,它的损失函数如下所示: 对于这个损失函数,一般有梯度下降法和最小二乘法两种极小化损失函数的优化方法,而scikit-le ...

  6. tensorflow模型的保存与恢复,以及ckpt到pb的转化

    转自 https://www.cnblogs.com/zerotoinfinity/p/10242849.html 一.模型的保存 使用tensorflow训练模型的过程中,需要适时对模型进行保存,以 ...

  7. 实用:Java基础流计算

    java的流不常用,每次学习完都懂,过了一段时间就全忘了... 记录下一点实用的东西... 需求: 截取文件的前250kb内容 public static void main(String[] arg ...

  8. 【CEOI1999】Sightseeing trip

    Description https://loj.ac/problem/10072 Solution 现在我连普及组题都不会了?(bushi) 懒得讲了,看这吧.

  9. a 标签的download 属性在谷歌浏览器下无法下载图片,如何处理?

    a 标签的download属性在下载图片文件的时候是如何的方便,可是可是谷歌浏览器不支持下载,而是下载打开新窗口预览图片.这个兼容性问题如何解决呢? 了解了一番,HTMLCanvasElement.t ...

  10. 编译teamtalk遇到的问题

    一.编译log4cxx遇到的问题 1.error: narrowing conversion 这是在gcc-6下面一个官方的错误 解决方法 https://issues.apache.org/jira ...