• 基于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
    1. from Page import Base
    2. # 创建LoginPage类继续BasePage类
    3. class LoginPage(Base.BasePage):
    4. '''统一平台登录Page层,登录页面封装操作到的元素'''
    5. '''第二层:页面元素进行分离,每个元素只定位一次,操作元素动作分离'''
    6. # 定义url变量,供父类中的open()方法使用
    7. url ="https://test01....cn/#/login"
    8. # 用户名输入框定位
    9. def form_username(self,user_name):
    10. return self.by_id("name").send_keys(user_name) # 使用了父类的self.by_id()方法定位元素,简洁了不少
    11. # 密码输入框定位
    12. def form_password(self,pass_word):
    13. return self.by_id("password").send_keys(pass_word)
    14. # 登录按钮定位
    15. def button_login(self):
    16. return self.by_xpath("//*[text()='登录']").click()

     test_login.py

    1. from Page import login_page
    2. import unittest
    3. from selenium import webdriver
    4. from time import sleep
    5. from selenium.webdriver.common.keys import Keys
    6. from CommonMethod import LogUtil
    7. logger = LogUtil.logs() # 调用封装的日志方法,将日志输出到控制台以及写入文件
    8. class LoginCase(unittest.TestCase):
    9. '''第三层:用单元测试框架对业务逻辑进行测试'''
    10. '''使用LoginPage类及它所继承的父类中的方法'''
    11. @classmethod
    12. def setUpClass(cls):
    13. # 实例化webdriver,俗称:打开浏览器
    14. cls.driver = webdriver.Firefox(executable_path='E:\\UI test\\UnittestProject\\Driver\\geckodriver.exe')
    15. cls.driver.implicitly_wait(10)
    16. @classmethod
    17. def tearDownClass(cls):
    18. cls.driver.quit()
    19. def test_login_success(self):
    20. page = login_page.LoginPage(self.driver) # 需要用到哪个Page类时,只需要将它传入浏览器驱动,就可以使用该类中提供的方法了
    21. page.open()
    22. page.form_username("XXX")
    23. page.form_password("123456")
    24. page.button_login()
    25. sleep(2)
    26. self.assertEqual(page.get_current_url(), "https://test01....cn/#/home")
    27. print("登录成功,用例执行结果通过,当前的url为"+ page.get_current_url())
    28. sleep(1)
    29. def test_login_fail(self):
    30. page = login_page.LoginPage(self.driver)
    31. page.open()
    32. page.form_username("XXX11")
    33. page.form_password("123456")
    34. page.button_login()
    35. self.assertNotEqual(page.get_current_url(), "https://test01....cn/#/home")
    36. print("登录失败,用例执行结果通过,当前的url为"+ page.get_current_url())
    37. page.form_username(Keys.CONTROL+'a') # 输入组合键Ctrl+a,全选输入框内容
    38. page.form_username(Keys.BACK_SPACE) # 删除键,删除选中的内容
    39. page.form_password(Keys.CONTROL + 'a')
    40. page.form_password(Keys.BACK_SPACE)
    41. sleep(1)
    42. if __name__ == '__main__':
    43. 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断言

    1. # 登录失败封装
    2. def button_login_fail(self):
    3. self.by_xpath("//span[text()='登录']").click()
    4. toast = self.by_xpath("//p[text()='账号或密码错误!']").text
    5. return toast
    6. # 登录成功封装
    7. def button_login_success(self):
    8. self.by_xpath("//span[text()='登录']").click()
    9. sleep(2)
    10. windows = self.driver.window_handles# 获取打开的多个窗口句柄
    11. self.driver.switch_to.window(windows[-1])# 切换到当前最新打开的窗口
    12. indexurl = self.get_current_url()
    13. return indexurl

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

    1. '''断言跳转的地址,通过try except语句块来进行测试断言,在实际自动化测试脚本开发中,经常要用到处理异常'''
    2. try:
    3. self.assertEqual(indexurl,"https://qa-xxxt/add")
    4. print("点击创建,正确跳转到新页面" + indexurl)
    5. except AssertionError as msg:
    6. print("没有跳转到正确页面,当前跳转的地址为"+addurl+"\n报错信息如下"+format(msg))
    7. '''当断言失败时会抛出异常测试用例执行失败,输出提示信息后重新将异常抛出,即raise,
    8. 若不重新抛出,用例则永远是显示执行成功的,因为它把异常处理掉了'''
    9. raise msg
    1. try:
    2. self.assertEqual(toast, "账号或密码错误!")
    3. print("登录失败用例场景执行通过,正确弹出提示信息为:" + toast)
    4. except AssertionError as msg:
    5. print("错误提示语与预期结果不一致,请检查"+ format(msg))
    6. raise msg
    1. try:
    2. self.assertEqual(toast, "账号或密码错误!")
    3. print("登录失败用例场景执行通过,正确弹出提示信息为:" + toast)
    4. except AssertionError as msg:
    5. print("错误提示语与预期结果不一致,请检查"+ format(msg))
    6. raise msg
    总结:

    感谢每一个认真阅读我文章的人!!!

    作为一位过来人也是希望大家少走一些弯路,如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。

    软件测试面试小程序

    被百万人刷爆的软件测试题库!!!谁用谁知道!!!全网最全面试刷题小程序,手机就可以刷题,地铁上公交上,卷起来!

    涵盖以下这些面试题板块:

    1、软件测试基础理论 ,2、web,app,接口功能测试 ,3、网络 ,4、数据库 ,5、linux

    6、web,app,接口自动化 ,7、性能测试 ,8、编程基础,9、hr面试题 ,10、开放性测试题,11、安全测试,12、计算机基础

     

     

                                                              全套资料获取方式:点击下方小卡片自行领取即可

  • 相关阅读:
    【allegro 17.4软件操作保姆级教程八】布线操作基础之三
    【开源】基于Vue和SpringBoot的校园失物招领管理系统
    视频监控管理平台EasyCVR自动注销后,页面还存留播放窗口是什么原因?解决办法是什么?
    Could not find androidx.camera:camera-view
    使用paddleX体验
    java---多线程编程一
    华硕ROG吹雪和微星刀锋钛两者如何选择
    CTFshow 信息收集 web 6 7 8 9 10
    自行车租赁管理系统
    从权限系统的菜单管理看算法和数据结构
  • 原文地址:https://blog.csdn.net/qq_73332379/article/details/133991005