基于Python+Selenium+Unittest+PO设计模式

一、什么是PO设计模式(Page Object Model)
1、Page Object是一种设计模式,它主要体现在对界面交互细节的封装上,使测试用例更专注于业务的操作,从而提高测试用例的可维护性。

2、一般PO设计模式有三层

第一层:

对Selenium 进行二次封装,定义一个所有页面都继承的 BasePage ,
封装 Selenium 基本方法 例如:元素定位,元素等待,导航页面 ,
不需要全部封装,用到多少方法就封装多少方法。
第二层:

页面元素进行分离,每个元素只定位一次,隔离定位,如果页面改变,只需要改变相应的元素定位;
业务逻辑分离 或 操作元素动作分离
第三层:

使用单元测试框架对业务逻辑进行测试

二、为什么要使用PO设计模式
页面频繁变化,(页面html结构等变化)导致页面UI元素频繁变动,元素定位改变
传统线性自动化(面向过程开发),用例中需要反复的定位同一个元素
每当页面发生变化的时候,需要在用例中寻找变动的部分,工作量大,容易产生遗漏,不容易维护
三、PO设计模式的六大原则
公共方法代表页面提供的服务
不要暴露细节
不要在封装的框架中做断言
方法可以return到新打开的页面
不要对所有元素建模,仅对自己关注的元素建模
相同的行为会产生不同的结果,可以封装不同的结果
四、PO设计模式实例
以公司的统一登录作为项目例子,用PO设计模式实现登陆:

1、手工用例:

2、用PO模式实现自动化用例
项目目录

Base.py
login_page.py

from Page import Base


# 创建LoginPage类继续BasePage类

class LoginPage(Base.BasePage):

'''统一平台登录Page层,登录页面封装操作到的元素'''

'''第二层:页面元素进行分离,每个元素只定位一次,操作元素动作分离'''

# 定义url变量,供父类中的open()方法使用

url ="https://test01....cn/#/login"

# 用户名输入框定位

def form_username(self,user_name):

return self.by_id("name").send_keys(user_name) # 使用了父类的self.by_id()方法定位元素,简洁了不少

# 密码输入框定位

def form_password(self,pass_word):

return self.by_id("password").send_keys(pass_word)

# 登录按钮定位

def button_login(self):

return self.by_xpath("//*[text()='登录']").click()

 test_login.py


from Page import login_page

import unittest

from selenium import webdriver

from time import sleep

from selenium.webdriver.common.keys import Keys

from CommonMethod import LogUtil


logger = LogUtil.logs() # 调用封装的日志方法,将日志输出到控制台以及写入文件


class LoginCase(unittest.TestCase):

'''第三层:用单元测试框架对业务逻辑进行测试'''

'''使用LoginPage类及它所继承的父类中的方法'''

@classmethod

def setUpClass(cls):

# 实例化webdriver,俗称:打开浏览器

cls.driver = webdriver.Firefox(executable_path='E:\\UI test\\UnittestProject\\Driver\\geckodriver.exe')

cls.driver.implicitly_wait(10)

@classmethod

def tearDownClass(cls):

cls.driver.quit()

def test_login_success(self):

page = login_page.LoginPage(self.driver) # 需要用到哪个Page类时,只需要将它传入浏览器驱动,就可以使用该类中提供的方法了

page.open()

page.form_username("XXX")

page.form_password("123456")

page.button_login()

sleep(2)

self.assertEqual(page.get_current_url(), "https://test01....cn/#/home")

print("登录成功,用例执行结果通过,当前的url为"+ page.get_current_url())

sleep(1)

def test_login_fail(self):

page = login_page.LoginPage(self.driver)

page.open()

page.form_username("XXX11")

page.form_password("123456")

page.button_login()

self.assertNotEqual(page.get_current_url(), "https://test01....cn/#/home")

print("登录失败,用例执行结果通过,当前的url为"+ page.get_current_url())

page.form_username(Keys.CONTROL+'a') # 输入组合键Ctrl+a,全选输入框内容

page.form_username(Keys.BACK_SPACE) # 删除键,删除选中的内容

page.form_password(Keys.CONTROL + 'a')

page.form_password(Keys.BACK_SPACE)

sleep(1)

if __name__ == '__main__':

unittest.main(verbosity=2)

执行结果

在test_login.py中有调用封装的日志方法,这里把封装的日志附上,在CommonMethod目录下的LogUtil.py

 LogUtil

 五、其他补充
1、相同的行为会产生不同的结果,可以封装不同的结果:在login_page针对【登录】按钮封装了2个方法

2、方法可以return到新打开的页面:在login_page针对【登录】按钮封装,封装了之后要return新页面或其他信息。test_login调用时命名变量来接收这个函数就行了,比如indexurl = page.button_login_success(),在后面断言可以用indexurl变量来跟预期的url断言


# 登录失败封装

def button_login_fail(self):

self.by_xpath("//span[text()='登录']").click()

toast = self.by_xpath("//p[text()='账号或密码错误!']").text

return toast


# 登录成功封装

def button_login_success(self):

self.by_xpath("//span[text()='登录']").click()

sleep(2)

windows = self.driver.window_handles# 获取打开的多个窗口句柄

self.driver.switch_to.window(windows[-1])# 切换到当前最新打开的窗口

indexurl = self.get_current_url()

return indexurl

3、断言:可以通过url、页面标题、text来断言


'''断言跳转的地址,通过try except语句块来进行测试断言,在实际自动化测试脚本开发中,经常要用到处理异常'''

try:

self.assertEqual(indexurl,"https://qa-xxxt/add")

print("点击创建,正确跳转到新页面" + indexurl)

except AssertionError as msg:

print("没有跳转到正确页面,当前跳转的地址为"+addurl+"\n报错信息如下"+format(msg))

'''当断言失败时会抛出异常测试用例执行失败,输出提示信息后重新将异常抛出,即raise,

若不重新抛出,用例则永远是显示执行成功的,因为它把异常处理掉了'''

raise msg

try:

self.assertEqual(toast, "账号或密码错误!")

print("登录失败用例场景执行通过,正确弹出提示信息为:" + toast)

except AssertionError as msg:

print("错误提示语与预期结果不一致,请检查"+ format(msg))

raise msg

try:

self.assertEqual(toast, "账号或密码错误!")

print("登录失败用例场景执行通过,正确弹出提示信息为:" + toast)

except AssertionError as msg:

print("错误提示语与预期结果不一致,请检查"+ format(msg))

raise msg

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值