UI自动化测试之页面对象设计模式


1、概述

1.1页面对象设计模式的优势

页面对象设计模式(page object)又叫po模式,PO模式是一种自动化测试设计模式,将页面定位和业务操作分开,也就是把对象定位和测试脚本分开,从而提供可维护性。核心思想是通过对界面元素的封装减少冗余代码,主要体现在对界面交互细节的封装,也就是在实际测试中只关注业务流程;同时在后期维护中,若元素定位发生变化, 只需要调整页面元素封装的代码,提高测试用例的可维护性、可读性。在自动化测试过程中,对于维护的成本而言,需要考虑进一步的优化,那么我们可以使用页面对象设计模式,它的具体优势如下:

(1)创建可以跨多个测试用例共享的代码

(2)减少重复代码的数量

(3)如果用户界面发生了维护,我们只需要维护一个地方,这样修改以及维护的成本相对而言是比较低的。

1.2创建项目

在pycharm中创建项目的步骤如图:

 然后点击attch即可在project中看到新创建的项目:

 给已经创建好的项目配置python解释器:

 2、页面对象设计

2.1目录结构设计

编写的目录顺序可自行调整,如下图:

各个目录详解:

(1)base:基础层,主要编写底层定位元素的类,它是一个包。

(2)common:公共类,里面编写公共使用到的方法。

(3)config:配置文件存储目录。

(4)data:存储测试使用到测试数据。

(5)page:对象层,编写具体的业务逻辑,把页面每一个操作行为单独的写一个方法或者是函数。

(6)report:测试报告目录,主要用来存放测试报告。

(7)test:测试层,里面主要是测试模块,也可以说是每个测试的场景的代码。

(8)utils:工具类,存放工具,如说明文档等。

2.2具体页面设计模式

这里以新浪邮箱为例,来说明整个页面设计的具体目录如何实现的代码。

2.2.1基础层:base

基础层,主要编写底层定位元素的类,它是一个包;元素定位有两类,单个元素定位和多个元素定位。以及一种特殊的情况,定位iframe框架中的元素,我们之前已经说过,进入iframe框架有三种方式:ID、name和索引,这里简述通过ID进入iframe框架的方式:

#base:基础层,主要编写底层定位元素的类,它是一个包;元素定位有两类,单个元素定位和多个元素定位,还有一种特殊情况,即需要定位的元素在iframe框架中。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.expected_conditions import NoSuchElementException
import time
#定义一个编写底层定位元素的类
class WebUI(object):
	def __init__(self,driver):
#=driver里的drive可以理解为webdriver实例化后的对象,这里只是做一个假设,在测试类中进行验证,
		self.driver=driver

		'''单个元素定位的方式
			args:*args:识别元素属性,ctr+鼠标放置到元素+左键:判断定位元素属性的方法是否正确
			:return:它是一个元组,需要带上具体什么方式定位元素属性以及元素属性的值'''

#单个元素定位:
	# def findElement(self,*args):
	# 	return self.driver.find_element(*args)

	# 如果想要获取到运行异常是的详细信息,可以使用函数异常逻辑,except返回具体错误信息。
	def findElement(self,*args):
		try:
			return self.driver.find_element(*args)
		except NoSuchElementException as e:
			return e.args[0]

		'''单个元素定位的方式
			args:*args:识别元素属性,ctr+鼠标放置到元素+左键:判断定位元素属性的方法是否正确
			index:识别元素的索引
			:return:它是一个元组,需要带上具体什么方式定位元素属性以及元素属性的值'''

# #多个元素定位,由于需要使用到他的索引,所以需要加一个index参数。
# 	def findElements(self, *args, index):
# 		return self.driver.find_elements(*args)[index]

# 如果想要获取到运行异常是的详细信息,可以使用函数异常逻辑,except返回具体错误信息。
	def findElements(self,*args,index):
		try:
			return self.driver.find_elements(*args)[index]
		except NoSuchElementException as e:
			return e.args[0]

#iframe框架中的元素定位,这里简述通过ID的方式进入iframe框架:
	def findFrame(self,frameID):
		return self.driver.switch_to.frame(frameID)

2.2.2对象层:page

对象层,编写具体的业务逻辑,把页面每一个操作行为单独的写一个方法或者是函数,他是一个包。

#page:对象层,编写具体的业务逻辑,把页面每一个操作行为单独的写一个方法或者是函数。如下:
from selenium.webdriver.common.by import By
from base.base import WebUI
import time
#定义一个登录的类:username、password、loginEnter、errorPrompt是登录的类中的数据属性,可以在整个类中调用,调用的方式为:self.数据属性。
class Login(WebUI):
	username=(By.ID,"freename")
	password=(By.ID,"freepassword")
	loginEnter=(By.CLASS_NAME,"loginBtn")
	errorPrompt=(By.XPATH,"/html/body/div[3]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]")

#用户名:输入用户名这个操作的函数
	def InputUsername(self,username):
		time.sleep(3)
		self.findElement(*self.username).send_keys(username)

#密码:输入密码这个操作的函数
	def InputPassword(self,password):
		time.sleep(3)
		self.findElement(*self.password).send_keys(password)

#点击登陆这个操作的函数
	def loginClick(self):
		self.findElement(*self.loginEnter).click()

#获取提示框文本信息的函数
	def textInformation(self):
		return self.findElement(*self.errorPrompt).text

#封装:把新浪输入用户名和密码后点击登录的操作封装为一个函数。
	def sinaLogin(self,username,password):
		self.InputUsername(username=username)
		self.InputPassword(password=password)
		self.loginClick()

2.2.3测试层:test

测试层,里面主要是测试模块,也可以说是每个测试的场景的代码。

#test:测试层,里面主要是测试模块,也可以说是每个测试的场景的代码。
from selenium import webdriver
import unittest
from page.login import Login
import time
#定义一个测试类:
class UnitTest(unittest.TestCase,Login):
#初始化
	def setUp(self) -> None:
		self.driver=webdriver.Chrome()    #这里是对webdriver进行实例化,也是对基础层中driver假设的证明。
		self.driver.maximize_window()
		self.driver.get("https://mail.sina.com.cn/")
		self.driver.implicitly_wait(30)
#清理
	def tearDown(self) -> None:
		self.driver.quit()

#测试模块:登陆的用户名和密码为空
	def test_login_Empty(self):    #测试用例的名称一定要以test开头,推荐test_。
		self.InputUsername(username="")   #调用page中的输入用户名的函数
		self.InputPassword(password="")   #调用page中的输入密码的函数
		self.loginClick()     #调用page中点击登录的函数
		# self.sinaLogin(username="",password="")     #调用在page中封装的函数sinaLogin()。
		time.sleep(5)
		self.assertEqual(self.textInformation(),"请输入邮箱名")    #断言,验证登录提示的文本信息,调用page中的获取提示框文本信息的函数
		time.sleep(5)

#测试模块:登陆的用户名和密码格式不对
	def test_login_format(self):    #测试用例的名称一定要以test开头,推荐test_。
		self.InputUsername(username="asd123")      #调用page中的输入用户名的函数
		self.InputPassword(password="asd123")     #调用page中的输入密码的函数
		self.loginClick()     #调用page中点击登录的函数
		# self.sinaLogin(username="asd123",password="asd123")    #调用在page中封装的函数sinaLogin()。
		time.sleep(5)
		self.assertEqual(self.textInformation(),"您输入的邮箱名格式不正确")    ##断言,验证登录提示的文本信息,调用page中的获取提示框文本信息的函数
		time.sleep(5)

#测试模块:登陆的用户名和密码错误
	def test_login_error(self):    #测试用例的名称一定要以test开头,推荐test_。   #调用在page中封装的函数sinaLogin()。
		self.InputUsername(username="asd123@sina.com")    #调用page中的输入用户名的函数
		self.InputPassword(password="asd123")     #调用page中的输入密码的函数
		self.loginClick()     #调用page中点击登录的函数
		# self.sinaLogin(username="asd123@sina.com",password="asd123")
		time.sleep(5)
		self.assertEqual(self.textInformation(),"登录名或密码错误")     ##断言,验证登录提示的文本信息,调用page中的获取提示框文本信息的函数
		time.sleep(5)