(一)POM模式介绍

1、什么是POM介绍

POM是Page Object Model页面对象模型的简称。

POM是为Web UI元素创建Object Repository的设计模式 。

在这个模型下,对于应用程序中的每个网页,应该有相应的页面类。

Page类将会找到该Web页面的WebElements,并且还包含对这些WebElements执行操作的页面方法。

POM设计模式旨在为每个待测试的页面创建一个页面对象,将那些繁琐的定位操作封装到这个页面对象中,只对外提供必要的操作接口,是一种封装思想。

白话总结:

我们所做的自动化测试,就是模拟人在浏览器上的操作。而自动化测试中操作所有的元素的步骤,无非就是先定位到页面的各种元素,然后在模拟各种对元素执行的操作。

而我们大量的工作都用在定位元素上,定位元素的方式有很多中,定位起来也非常的繁琐。如果将这些代码全部放在代码中,不去好好的管理,代码会显示非常的冗余,而且不容易维护。所以将这些繁琐的定位,封装到一些页面对象中,用例只需要去调用就可以了。

2、为什么要使用POM模式

少数的自动化测试用例维护起来看起来是很容易的。但随着时间的迁移,测试套件将持续的增长脚本也将变得越来越臃肿庞大。如果变成我们需要维护10个页面,100个页面,甚至1000个呢?而且页面元素很多是公用的,所以页面元素的任何改变都会让我们的脚本维护变得繁琐复杂,而且变得耗时易出错。

也就是说页面中有一个按钮"元素A"。该元素A在十个测试用例中都被用到了,如果元素A被前端更新了,我就要去修改这十个自动化用例所用道元素A的地方。如果有100个、1000个用例用到了元素A,那我可就疯了。

而POM设计模式,会把公共的元素抽取出来,该元素被前端修改,只需要更新该元素的定位方式即可,用例不需要改动。换句话说,不管我多少测试用例,用到了该元素,我只重新修改元素的定位方式,重新能够获得该元素即可。

3、POM的优势

在自动化测试中,引入了Page Object Model(POM):页面对象模式,能让我们的测试代码变得可读性更好,高可维护性,高复用性。

还有如下优势:

  1. 让Ul自动化更早介入项目中,可项目开发完再进行元素定位的适配与调试。

    换句话说元素定位器分离出来写,最后根据前端开发出来的页面,再根据页面编写编写元素定位器,前期可以做一些其他的工作。
  2. POM设计模式将页面元素定位和业务操作流程分开,分离了测试对象和测试脚本(对象库与用例分离),使得我们更好的复用测试对象。
  3. 如果Ul页面元素更改,测试脚本不需要更改,只需要更改页面对象中的某些代码就可以。
  4. POM设计模式能让我们的测试代码变得更加优化,提高了可读性,可维护性,可复用性。
  5. 可多人共同维护开发脚本,利于团队协作。

4、POM模式封装思路

(1)POM模式将页面分成三层

  1. 表现层:页面中可见的元素,都属于表现层。(元素定位器的编写)
  2. 操作层:对页面可见元素的操作。点击、输入、拖拽等。
  3. 业务层:在页面中对若干元素操作后所实现的功能。(就是测试用例)

(2)POM模式的核心要素(重点)

  1. 在POM模式中将公共方法统一封装成到一个BasePage 类中,换句话说该基类对Selenium的常用操作做二次封装。
  2. 每个页面对应一个Page类,Page类都需要继承BasePage,通过 driver 来管理本Page类中的元素,并将Page类中的操作封装成一个个的方法。

    换句话说,就是Page类中封装页面表现层和操作层。
  3. TestCase继承 unittest.Testcase 类,并且依赖 Page 类,从而实现相应的测试步骤。

(3)总结

就是按照系统或模块 —> 其中包含哪些被测页面 —> 页面中的哪些元素

换句话说,元素被页面管理,页面被模块管理。

  • 根据页面来进行管理例:

    例如:测式xx页面,需要用到的元素,把所有的元素定位器编写出来。
  • 页面根据系统或者模块来管理例如:

    例如:xx系统或模块,涉及到哪几个页面元素。

(4)非POM和POM对比图

(5)POM设计模式核心架构图

5、对POM小结:

  • POM是selenium webdriver自动化测试实践对象库设计模式。
  • POM使得测试脚本更易于维护。
  • POM通过对象库方式进一步优化了元素、用例、数据的维护组织。

(二)将普通的Selenium代码封装成POM模式

1、案例说明:

提示:这里只是提供一种封装的思路,小伙伴们可以根据自己的实际情况,按需封装。

以下是简单普通的登录测试用例

# 1. 导入包
from selenium import webdriver
import time # 2. 打开谷歌浏览器(获取浏览器操作对象)
driver = webdriver.Chrome() # 3. 打开快递100网站
url = "https://sso.kuaidi100.com/sso/authorize.do"
driver.get(url)
time.sleep(3) # 4. 登陆网站
driver.find_element_by_id("name").send_keys('xxxxxxxxxxx')
driver.find_element_by_id("password").send_keys('xxxxxx')
driver.find_element_by_id("submit").click()
time.sleep(3) # 5. 关闭浏览器
driver.quit()

那我们如何进行一个改造升级呢?

2、加入unittest测试框架

# 1. 导入包
from selenium import webdriver
import time
import unittest # 定义测试类
class TestCaseLogin(unittest.TestCase):
def setUp(self) -> None:
"""
前置函数
用于打开浏览器,连接数据库,初始化数据等操作
"""
# 2. 打开谷歌浏览器(获取浏览器操作对象)
self.driver = webdriver.Chrome() # 3. 打开快递100网站
url = "https://sso.kuaidi100.com/sso/authorize.do"
self.driver.get(url)
time.sleep(3) def tearDown(self) -> None:
"""
后置函数
用于关闭浏览器,断开数据库连接,清理测试数据等操作
"""
# 5. 关闭浏览器
self.driver.quit() def testLogin(self):
"""登陆测试用例"""
self.driver.find_element_by_id("name").send_keys('xxxxxxxxxxx')
self.driver.find_element_by_id("password").send_keys('xxxxxx')
self.driver.find_element_by_id("submit").click()
time.sleep(3) if __name__ == '__main__':
unittest.main()

如果有不清楚unittest测试框架的小伙伴可以查看我以前的unittest测试框架博客有4篇,简单易懂。

3、加入元素显示等待

我们上边的示例中,用的是固定的等待时间,我们需要有话一下代码的效率,加入元素的显示等待。

关于元素显示等待请看:元素等待的使用

Seleniun的EC模块:EC模块的使用

# 1. 导入包
from selenium import webdriver
import time
import unittest
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC # 定义测试类
class TestCaseLogin(unittest.TestCase):
def setUp(self) -> None:
"""
前置函数
用于打开浏览器,连接数据库,初始化数据等操作
"""
# 2. 打开谷歌浏览器(获取浏览器操作对象)
self.driver = webdriver.Chrome() # 3. 打开快递100网站
url = "https://sso.kuaidi100.com/sso/authorize.do"
self.driver.get(url)
time.sleep(2) def tearDown(self) -> None:
"""
后置函数
用于关闭浏览器,断开数据库连接,清理测试数据等操作
"""
# 5. 关闭浏览器
time.sleep(2)
self.driver.quit() def testLogin(self):
"""登陆测试用例"""
# 编写定位器
name_input_locator = ("id", "name")
passwd_input_locator = ("id", "password")
submit_button_locator = ("id", "submit") # 等待元素出现在操作元素
WebDriverWait(self.driver, 5).until(EC.visibility_of_element_located(name_input_locator))
WebDriverWait(self.driver, 5).until(EC.visibility_of_element_located(passwd_input_locator))
WebDriverWait(self.driver, 5).until(EC.visibility_of_element_located(submit_button_locator))
self.driver.find_element_by_id("name").send_keys('xxxxxxxxxxx')
self.driver.find_element_by_id("password").send_keys('xxxxxx')
self.driver.find_element_by_id("submit").click() if __name__ == '__main__':
unittest.main()

4、引入POM模式

我们发现上面的代码越来越乱,代码冗余,不利于维护,可读性差,不可复用。

(1)改造案例思路:

  • 第一, 我们要分离测试对象(元素对象)和测试脚本(用例脚本),那么我们分别创建两个脚本文件,分别为:

    • LoginPage.py 用于定义页面元素对象,每一个元素都封装成组件(可以看做存放页面元素对象的仓库)
    • TestCaseLogin.py 测试用例脚本。
  • 第二,抽取出公共方法定义在base.py文件中,每个Page类都要继承这个base.py文件,也就是每Page类都能使用base类中的方法,来操作页面中的元素,同时也可以在每个Page类中定义自己独有的方法,解决工作中的实际需求。
  • 第三,设计实现思想,一切元素和元素的操作组件化定义在Page页面,用例脚本页面,通过调用Page中的组件对象,进行拼凑成一个登录脚本。

(2)封装公共操作在base类

把一些公共的方法放到此类中,这个类将被PO对象继承。

"""
封装公共方法
"""
from selenium import webdriver
import time
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC class Base:
def __init__(self, browser="chrome"):
"""
初始化driver
:param browser:浏览器名称
"""
if browser == "chrome":
self.driver = webdriver.Chrome()
elif browser == "firefox":
self.driver = webdriver.Firefox()
elif browser == "ie":
self.driver = webdriver.Ie()
else:
self.driver = None
print("请输入正确的浏览器,例如:chrome,firefox,ie") def open_url(self, url):
"""
打开地址
:param url: 被测地址
:return:
"""
self.driver.get(url)
time.sleep(2) def find_element(self, locator, timeout=10):
"""
定位单个元素,如果定位成功返回元素本身,如果失败,返回False
:param locator: 定位器,例如("id","id属性值")
:return: 元素本身
"""
try:
element = WebDriverWait(self.driver, timeout).until(EC.presence_of_element_located(locator))
return element
except:
print(f"{locator}元素没找到")
return False def click(self, locator):
"""
点击元素
:return:
"""
element = self.find_element(locator)
element.click() def send_keys(self, locator, text):
"""
元素输入
:param locator: 定位器
:param text: 输入内容
:return:
"""
element = self.find_element(locator)
element.clear()
element.send_keys(text) def close(self):
"""
关闭浏览器
:return:
"""
time.sleep(2)
self.driver.quit() if __name__ == '__main__':
base = Base()
base.open_url("https://sso.kuaidi100.com/sso/authorize.do")
base.close()

(3)每个页面对应一个Page类

定位元素的定位器和操作元素方法分离开,元素定位器全部放一起,然后每一个操作元素动作写成一个方法。

"""
管理登陆页面所有的元素,
以及操作这些元素所用的方法。
"""
from common.base import Base class LoginPage(Base):
# 编写定位器和页面属性
name_input_locator = ("id", "name")
passwd_input_locator = ("id", "password")
submit_button_locator = ("id", "submit")
username = 'xxxxxxxxxxx'
userpasswd = 'xxxxxx'
url = 'https://sso.kuaidi100.com/sso/authorize.do' # """封装元素操作"""
# 输入用户名
def name_imput(self):
self.send_keys(self.name_input_locator, self.username) # 输入密码
def passwd_imput(self):
self.send_keys(self.passwd_input_locator, self.userpasswd) # 点击登陆
def click_submit(self):
self.click(self.submit_button_locator) if __name__ == '__main__':
base = Base('firefox')
base.open_url(url=LoginPage.url)

(4)原登陆案例封装完成代码

测试方法及测试类的执行都在此文件中。

# 1. 导入包
import unittest
from pages.login_page import LoginPage # 定义测试类
class TestCaseLogin(unittest.TestCase):
def setUp(self) -> None:
self.driver = LoginPage()
self.driver.open_url(LoginPage.url) def tearDown(self) -> None:
# 5. 关闭浏览器
self.driver.close() def testLogin(self):
"""登陆测试用例"""
self.driver.name_imput()
self.driver.passwd_imput()
self.driver.click_submit() if __name__ == '__main__':
unittest.main()

提示:最后我们在使用测试套件来执行测试用例的时候,就定位这些testcase文件就好。

5、总结

虽然该实现方法看上去复杂多了,但其中的设计好处是不同层关心不同的问题。

  • 页面对象只关心元素的定位。
  • 测试用例只关心测试数据。

使用POM进行重新构造代码结构后,发现代码测试用例代码的可读性提高很多。

定义好的PageObject组件可以重复在其它的脚本中进行使用,减少了代码的工作量,也方便对脚本进行后期的维护管理,当元素属性发生变化时,我们只需要对一个PageObaject页面中的对象组件定义进行更改即可。

『心善渊』Selenium3.0基础 — 30、UI自动化测试之POM设计模式的更多相关文章

  1. 『心善渊』Selenium3.0基础 — 24、Selenium的expected_conditions模块详细介绍

    目录 1.EC模块介绍 2.EC模块常用类 3.EC模块的使用 4.EC模块综合使用 (1)title_is(title)示例 (2)presence_of_element_located(locat ...

  2. 『心善渊』Selenium3.0基础 — 11、Selenium对元素常用操作

    目录 1.Selenium对元素常用操作 2.Selenium对元素的其他操作 1.Selenium对元素常用操作 操作 说明 click() 单击元素 send_keys() 模拟输入 clear( ...

  3. 『心善渊』Selenium3.0基础 — 12、Selenium操作鼠标和键盘事件

    目录 (一)对鼠标的操作 1.鼠标事件介绍 2.ActionChains 类鼠标操作的常用方法 3.perform()方法 4.鼠标事件操作步骤 5.示例 (1)右键单击.左键双击 (2)鼠标拖拽动作 ...

  4. 『心善渊』Selenium3.0基础 — 1、Selenium自动化测试框架介绍

    目录 1.Selenium介绍 2.Selenium的特点 3.Selenium版本说明 4.拓展:WebDriver与Selenium RC的区别 5.Webdriver工作原理 1.Seleniu ...

  5. 『心善渊』Selenium3.0基础 — 23、Selenium元素等待

    目录 1.什么是元素等待 2.为什么要设置元素等待 3.Selenium中常用的等待方式 4.强制等待 5.隐式等待 (1)隐式等待介绍 (2)示例 6.显式等待 (1)显式等待介绍 (2)语法 (3 ...

  6. 『心善渊』Selenium3.0基础 — 2、Selenium测试框架环境搭建(Windows)

    目录 1.浏览器安装 2.浏览器驱动下载 (1)ChromeDriver for Chrome (2)Geckodriver for Firefox (3)IEDriverServer for IE ...

  7. 『心善渊』Selenium3.0基础 — 3、使用Selenium操作浏览器对象的基础API

    目录 1.导入Selenium库 2.创建浏览器对象 3.浏览器窗口大小设置 4.浏览器位置设置 5.请求访问网址 6.浏览器页面前进.后退和刷新 7.关闭浏览器 相比于高大上的各种Selenium进 ...

  8. 『心善渊』Selenium3.0基础 — 4、Selenium基础元素定位详解

    目录 1.什么是元素定位 2.Selenium元素定位常用API (1)By_id 定位 (2)by_name 定位 (3)by_class_name 定位 (4)by_tag_name 定位 (5) ...

  9. 『心善渊』Selenium3.0基础 — 19、使用Selenium操作文件的上传和下载

    目录 1.Selenium实现文件上传 (1)页面中的文件上传说明 (2)文件上传示例 (3)总结 2.Selenium实现文件下载 (1)Firefox浏览器文件下载 1)操作步骤: 2)文件下载示 ...

随机推荐

  1. Linux C 文件IO

    文件IO 2021-05-31 12:46:14 星期一 目录 文件IO 基础IO open 错误 creat read 一个例子 write close lseek 文件空洞 unlink删除 io ...

  2. 太赞了!Python竟可以轻松实现音频格式无损转换

    大家好,我是辰哥 辰哥在平时处理音频格式的时候,需要去下载各种音频处理软件(专业一点的软件还要收费),掌握Python技术的我们,知道Python是万能的(哈哈哈,开个玩笑).今天辰哥就来教大家用Py ...

  3. Nginx实战部署常用功能演示(超详细版),绝对给力~~~

    前言 上次分享了一些开发过程中常用的功能,但如果到真实环境中,其实还需要一些额外的配置,比如说跨域.缓存.配置SSL证书.高可用等,老规矩,还是挑几个平时比较常用的进行演示分享.上篇详见Nginx超详 ...

  4. JMeter36个内置函数及11个新增函数介绍

    JMeter内置了36个函数,这些函数可以通过函数助手进行编辑和测试.了解这些函数,不仅能提高JMeter的使用熟练度,也有助于知晓测试工具或测试框架通用的函数有哪些,在自主设计时,作为参考借鉴. J ...

  5. python字典转bytes类型字典

    python字典转bytes类型字典import base64 import json 1. a={"Vod":{"userData":"{}&quo ...

  6. 前端基础——js数据类型及判断方法

    一.数据类型 我们通常熟知的数据类型有六种,包括5种基本数据类型(Number, String, Boolean, Undefined, Null)和一种引用数据类型(Object).ES6又新增了一 ...

  7. BERT模型的OneFlow实现

    BERT模型的OneFlow实现 模型概述 BERT(Bidirectional Encoder Representations from Transformers)是NLP领域的一种预训练模型.本案 ...

  8. 自动调试用于移动GPU的卷积网络

    自动调试用于移动GPU的卷积网络 对特定设备进行自动调试对于获得最佳性能至关重要.这是有关如何调试整个卷积网络的说明文档. TVM中Mobile GPU的算子实现以模板形式编写.模板具有许多可调旋钮( ...

  9. JVM集合之开篇点题

    大家在平时的开发过程中是否遇到过StackOverflowError.OutOfMemoryError等类似的内存溢出错误呢?大家又是怎么解决这个问题的?再来,大家在面试过程中有没有被面试官提问过jv ...

  10. springMVC源码解读笔记

    1: DispatcherServlet 的初始化流程(调用的init方法) a) 初始化spring高级容器,WebApplicationContext(容器初始化12个步骤) Servlet类的i ...