python+selenium自动化—Page Object模式
什么是Page Object Model模式
Page Object模式是Selenium中的一种测试设计模式,主要是将每一个页面设计为一个Class,其中包含页面中需要测试的元素(按钮,输入框,标题 等),这样在Selenium测试页面中可以通过调用页面类来获取页面元素,这样巧妙的避免了当页面元素id或者位置变化时,需要改测试页面代码的情况。 当页面元素id变化时,只需要更改测试页Class中页面的属性即可。
Page Object模式是一种自动化测试设计模式,将页面定位和业务操作分开,分离测试对象(元素对象)和测试脚本(用例脚本),提高用例的可维护性。
POM的优势
参见非POM和POM对比图

在自动化测试中,引入了Page Object Model(POM):页面对象模式来解决,POM能让我们的测试代码变得可读性更好,高可维护性,高复用性。
POM的优势
1. POM提供了一种在UI层操作、业务流程与验证分离的模式,这使得测试代码变得更加清晰和高可读性。
2. 对象库与用例分离,使得我们更好的复用对象,甚至能与不同的工具进行深度结合应用。
3. 可复用的页面方法代码会变得更加优化。
4. 更加有效的命名方式使得我们更加清晰的知道方法所操作的UI元素。例如我们要回到首页,方法名命名为: gotoHomePage(),通过方法名即可清晰的知道具体的功能实现。
案例说明
以下126邮箱登录的普通用例
from selenium import webdriver
def test_login_mail(): driver = webdriver.Chrome() driver.get("http://www.126.com") driver.find_element_by_id("idInput").clear() driver.find_element_by_id("xxxxxxx").send_keys("xxxxx") driver.find_element_by_id("xxxxxxx").clear() driver.find_element_by_id("xxxxxxx").send_keys("xxxxxx") driver.find_element_by_id("loginBtn").click()
如上是一个简单的小脚本,脚本维护看起来很简单。但随着时间测试套件的增长。随着你在代码中添加越来越多的行,事情变得艰难。那么如何进行改造成POM模式?
改造成POM模式的基础思路
1.我们要分离测试对象(元素对象)和测试脚本(用例脚本),那么我们分别创建两个脚本文件,分别为: LoginPage.py 用于定义页面元素对象,每一个元素都封装成组件(可以看做存放页面元素对象的仓库) CaseLoginTest.py 测试用例脚本。
2.设计实现思想,一切元素和元素的操作组件化定义在Page页面,用例脚本页面,通过调用Page中的组件对象,进行拼凑成一个登录脚本。
创建BasePage类
#! /usr/bin/python3 # coding:utf-8 from selenium import webdriver from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.common.by import By import time class Action(object): """基础类封装所有页面都公用的方法,例如driver,url,findElement等""" #初始化driver、url等 def __init__(self, selenium_driver, base_url): self.base_url = base_url self.driver = selenium_driver self.timeout = 30 def on_page(self): return self.driver.current_url == self.base_url #打开页面,校验页面链接是否加载正确 def _open(self, url): # 使用get打开访问链接 self.driver.get(url) self.driver.maximize_window() # 使用assert进行检验,打开的链接是否与配置的地址一致 assert self.on_page(), 'Did not land on %s' % url # 定义open方法,调用_open()进行打开链接 def open(self): self._open(self.base_url) # 重写元素定位方法 def find_element(self, *loc): # return self.driver.find_element(*loc) try: WebDriverWait(self.driver,10).until(lambda driver:driver.find_element(*loc).is_displayed()) return self.driver.find_element(*loc) except: print("%s页面中未能找到%s元素"%(self, loc)) # 重写switch_frame方法 def switch_frame(self, loc): return self.driver.switch_to_frame(loc) # 定义script方法,用于执行JS脚本,范围执行结果 def script(self, src): self.driver.execute_script(src) # 重写定义send_key方法 def send_keys(self, loc, vaule, clear_first=True, click_first=True): try: loc = getattr(self,"_%s"%loc) if click_first: self.driver.find_element(*loc).click() elif clear_first: self.driver.find_element(*loc).clear() self.driver.find_element(*loc).send_keys(vaule) else: pass except AttributeError: print("%s页面中未能找到%s元素"%(self, loc)) # .... # 脚本调试 if __name__ == '__main__': base_url = 'https://www.baidu.com/' input_kw = (By.ID, "kw") click_su = (By.ID, "su") driver = webdriver.Chrome() cc = Action(driver,base_url) cc.open() cc.find_element(*input_kw).send_keys("selenium") cc.find_element(*click_su).click() time.sleep(9) cc.driver.quit()
首先创建一个基础类BasePage,在初始化方法__init__()中定义驱动(driver)、基本的URL(base_url)和超过时间(timeout)等。
定义open()方法用于打开URL网站,但它本身并未做这件事情,而是交由_open()方法来实现。关于URL地址断言部分,则交由on_page()方法来实现,而find_element()方法用于元素的定位。
创建LoginPage类
#! /usr/bin/python3 # coding:utf-8 from selenium.webdriver.common.by import By import BasePage #继承BasePage类 class LoginPage(BasePage.Action): #定位器,通过元素属性定位元素对象 username_loc=(By.ID,"idInput") password_loc =(By.ID,"pwdInput") submit_loc =(By.ID,"loginBtn") span_loc=(By.CSS_SELECTOR,"div.error-tt>p") dynpw_loc =(By.ID,"lbDynPw") userid_loc =(By.ID,"spnUid") #Action def open(self): #调用page中的_open打开连接 self._open(self.base_url,self.pagetitle) #调用send_keys对象,输入用户名 def input_username(self, username): self.find_element(*self.username_loc).send_keys(username) #调用send_keys对象,输入密码 def input_password(self, password): self.find_element(*self.password_loc).send_keys(password) #调用send_keys对象,点击登录 def click_submit(self): self.find_element(*self.submit_loc).click() #用户名或密码不合理是Tip框内容展示 def show_span(self): return self.find_element(*self.span_loc).text #切换登录模式为动态密码登录(IE下有效) def swich_DynPw(self): self.find_element(*self.dynpw_loc).click() #登录成功页面中的用户ID查找 def show_userid(self): return self.find_element(*self.userid_loc).text #.......
LoginPage类中主要对登录页面上的元素进行封装,使其成为更具体的操作方法。例如,用户名、密码和登录按钮都被封装成了方法。
创建CaseLoginTest.py类
#! /usr/bin/python3 # -*- coding: utf-8-*- import unittest import LoginPage from selenium import webdriver class Caselogin126mail(unittest.TestCase): """登录case""" @classmethod def setUpClass(cls): cls.driver = webdriver.Chrome() cls.driver.implicitly_wait(30) cls.url = "http://xxxx.xxx.com" cls.username = "xxxxx" cls.password = "xxxxx" # 用例执行体 def test_login_mail(self): # 声明LoginPage类对象 login_page = LoginPage.LoginPage(self.driver, self.url) # 调用打开页面组件 login_page.open() # 调用用户名输入组件 login_page.input_username(self.username) # 调用密码输入组件 login_page.input_password(self.password) # 调用点击登录按钮组件 login_page.click_submit() @classmethod def tearDownClass(cls): cls.driver.quit() if __name__ == "__main__": unittest.main()
使用POM进行重新构造代码结构后,发现代码测试用例代码的可读性提高很多,元素写成组件的方式,不需要每次都写findElement直接在脚本中调用组件就可以使用。
浙公网安备 33010602011771号