当前位置:   article > 正文

自动化测试(UI)----PO设计模式_自动化测试模式

自动化测试模式

前言


PO模式是一种自动化测试设计模式,将页面定位和业务操作分开,也就是把对象定位和测试脚本分开,从而提供可维护性。

一、简介


PO是Page Object(页面对象)的缩写,PO模式是自动化测试项目开发实践的最佳设计模式之一,核心思想是通过对界面元素的封装减少冗余代码,主要体现在对界面交互细节的封装,也就是在实际测试中只关注业务流程;同时在后期维护中,若元素定位发生变化, 只需要调整页面元素封装的代码,提高测试用例的可维护性、可读性。

二、PO模式的三层结构


PO模式可以把一个页面分为三层,对象库层、操作层、业务层。

(一)对象库层
Base(基类):封装page 页面一些公共的方法,如初始化方法、查找元素方法、点击元素方法、输入方法、获取文本方法、截图方法等
å¨è¿éæå¥å¾çæè¿°

注意:

以上方法封装时候,解包只需一此,在查找元素解包*loc;
driver 为虚拟,谁调用 base 时,谁传入,无需关注从哪里来;
loc:真正使用 loc 的方法只有查找元素方法使用;


(二)操作层
page(页面对象):封装对元素的操作,一个页面封装成一个对象;

思路:继承 Base;
实现:
模块名: 实际操作模块名称 如:page_login.py
页面对象类名:以大驼峰方法将模块名抄进来,有下划线去掉下划线,如class PageLogin(Base);
方法:涉及元素,将每个元素操作单独封装成一个操作方法;
组装:根据需求组装以上操作步骤。


(三)业务层
scripts(业务层):将一个或多个操作组合起来完成一个业务功能。比如登录:需要输入帐号、密码、点击登录三个操作。

思路:导包调用 page 页面
实现:
模块:test+实际操作模块名称 如:test_login.py
测试业务类名:以大驼峰方法将模块名抄进来,有下划线去掉下划线,如class TestLogin(unittest.TestCase):
方法:
① 初始化方法 setUp() 注:在 unittest 框架中不能使用 def init()初始化方法;
#实例化 页面对象
#前置操作 如:打开网址等
② 结束方法 teardown
#关闭驱动
③ 测试方法
#根据要操作的业务来实现

三、实例


为更好的理解PO模式,下面采用版本迭代的方式来学习,便于对不同版本的优缺点进行对比和理解。

(一)版本
V1:不使用任何设计模式和单元测试框架(线性模型)
V2:使用UnitTest管理用例
V3:po 模式编写
V4: po模式优化 + 数据驱动


(二)案例说明
对TPshop项目的登录模块进行自动化测试,以账号不存在及密码错误为例作为测试用例。

账号不存在
点击首页的‘登录’链接,进入登录页面
输入一个不存在的用户名
输入密码
输入验证码
点击登录按钮
获取错误提示信息
密码错误
点击首页的‘登录’链接,进入登录页面
输入用户名
输入一个错误的密码
输入验证码
点击登录按钮
获取错误提示信息


(三)V1版本
示例代码

  1. """
  2. 登录功能-账号不存在
  3. """
  4. from selenium import webdriver
  5. # 创建浏览器驱动对象,并完成初始化操作
  6. driver = webdriver.Firefox()
  7. driver.maximize_window()
  8. driver.implicitly_wait(10)
  9. driver.get("http://localhost")
  10. # 点击首页的‘登录’链接,进入登录页面
  11. # 输入用户名
  12. driver.find_element_by_id("username").send_keys("11234567843")
  13. # 输入密码
  14. driver.find_element_by_name("password").send_keys("23615115")
  15. # 点击登录按钮
  16. driver.find_element_by_name("sbtbutton").click()
  17. # 获取提示信息
  18. msg = driver.find_element_by_class_name(".layui-layer-padding").text
  19. print("msg=", msg)
  20. expect_result ="账号不存在!"
  21. try:
  22. # 断言
  23. assert expect_result==msg
  24. print("result:",result)
  25. except AssertionError:
  26. # 截图
  27. driver.get_screenshot_as_file("./{}.png".format(time.strftime("%Y_%m_%d_%H_%M_%S")))
  28. print("result:",result)
  29. raise
  30. # 关闭驱动对象
  31. driver.quit()

 存在的问题
一条测试用例对应一个文件,用例较多时不方便管理维护
代码高度冗余

(四)V2版本

示例代码

  1. # -*-coding:utf-8 -*-
  2. # Auothor:yue_luo
  3. import time
  4. import unittest
  5. from time import sleep
  6. from selenium import webdriver
  7. class TestLogin(unittest.TestCase):
  8. """
  9. 对登录模块的功能进行测试
  10. """
  11. driver = None
  12. @classmethod
  13. def setUpClass(cls):
  14. cls.driver = webdriver.Chrome()
  15. cls.driver.maximize_window()
  16. cls.driver.implicitly_wait(10)
  17. cls.driver.get("http://www.tpshop.com")
  18. cls.driver.find_element_by_link_text("登录").click()
  19. @classmethod
  20. def tearDownClass(cls):
  21. cls.driver.quit()
  22. # 账号不存在
  23. def test_login_username_is_error(self):
  24. # 输入用户名
  25. username =self.driver.find_element_by_id("username")
  26. username.clear()
  27. username.send_keys("13099999999")
  28. # 输入密码
  29. password=self.driver.find_element_by_id("password")
  30. password.clear()
  31. password.send_keys("123456")
  32. # 输入验证码
  33. verify_code = self.driver.find_element_by_id("verify_code")
  34. verify_code.clear()
  35. verify_code.send_keys("8888")
  36. # 点击‘登录’
  37. self.driver.find_element_by_name("sbtbutton").click()
  38. # 断言提示信息
  39. msg = self.driver.find_element_by_class_name("layui-layer-content").text
  40. print("msg=", msg)
  41. try:
  42. self.assertIn("账号不存在!", msg)
  43. sleep(3)
  44. except AssertionError:
  45. self.driver.get_screenshot_as_file("./{}.png".format(time.strftime("%Y_%m_%d_%H_%M_%S")))
  46. raise
  47. finally:
  48. self.driver.find_element_by_css_selector(".layui-layer-btn0").click()
  49. def test_login_password_is_error(self):
  50. # 输入用户名
  51. username = self.driver.find_element_by_id("username")
  52. username.clear()
  53. username.send_keys("17864307785")
  54. # 输入密码
  55. password = self.driver.find_element_by_id("password")
  56. password.clear()
  57. password.send_keys("error")
  58. # 输入验证码
  59. verify_code = self.driver.find_element_by_id("verify_code")
  60. verify_code.clear()
  61. verify_code.send_keys("8888")
  62. # 点击‘登录’
  63. self.driver.find_element_by_name("sbtbutton").click()
  64. # 断言提示信息
  65. msg = self.driver.find_element_by_class_name("layui-layer-content").text
  66. print("msg=", msg)
  67. try:
  68. self.assertIn("密码错误!", msg)
  69. sleep(3)
  70. except AssertionError:
  71. self.driver.get_screenshot_as_file("./{}.png".format(time.strftime("%Y_%m_%d_%H_%M_%S")))
  72. raise
  73. finally:
  74. self.driver.find_element_by_css_selector(".layui-layer-btn0").click()

引入UnitTest的好处
方便组织、管理多个测试用例
提供了丰富的断言方法
方便生成测试报告
减少了代码冗余

存在的问题
代码冗余

(五)V3版本(PO模式)

代码结构图å¨è¿éæå¥å¾çæè¿°

示例代码

对象库层:base.py

  1. """base.py"""
  2. import time
  3. from selenium import webdriver
  4. from selenium.webdriver.support.wait import WebDriverWait
  5. class Base:
  6. # 初始化
  7. def __init__(self,driver):
  8. self.driver = driver
  9. # 查找元素方法
  10. def base_find_element(self,loc,timeout=30,poll_frequency=0.5):
  11. return WebDriverWait(driver=self.driver,timeout=timeout,poll_frequency=poll_frequency).until(lambda x:x.find_element(*loc))
  12. # 点击方法
  13. def base_click(self,loc):
  14. self.base_find_element(loc).click()
  15. # 输入方法
  16. def base_input(self,loc,value):
  17. element = self.base_find_element(loc)
  18. element.clear()
  19. element.send_keys(value)
  20. # 获取文本方法
  21. def base_get_text(self,loc):
  22. msg = self.base_find_element(loc).text
  23. return msg
  24. # 截图
  25. def base_get_image(self,):
  26. self.driver.get_screenshot_as_file("./{}.png".format(time.strftime("%Y_%m_%d_%H_%M_%S")))

操作层:_ _init _ _.py 和 page_login.py

  1. """__init__.py"""
  2. # -*-coding:utf-8 -*-
  3. # Auothor:yue_luo
  4. from selenium.webdriver.common.by import By
  5. """以下为服务器域名配置地址"""
  6. url ="http://www.tpshop.com"
  7. """以下为登录页面配置信息"""
  8. # 登录链接
  9. login_link = By.PARTIAL_LINK_TEXT , "登录"
  10. # 用户名
  11. login_username = By.ID,"username"
  12. # 密码
  13. login_pwd = By.NAME,"password"
  14. # 验证码
  15. login_verify_code = By.CSS_SELECTOR,"#verify_code"
  16. # 登录按钮
  17. login_btn =By.NAME,"sbtbutton"
  18. # 获取异常文本信息
  19. login_err_info = By.CLASS_NAME,"layui-layer-content"
  20. # 异常提示框 确定按钮
  21. login_err_btn_ok = By.CSS_SELECTOR,".layui-layer-btn0"
  1. """page_login.py"""
  2. from v3 import page
  3. from v3.base.base import Base
  4. class PageLogin(Base):
  5. # 点击登录链接
  6. def page_click_login_link(self):
  7. self.base_click(page.login_link)
  8. # 输入用户名
  9. def page_input_username(self,username):
  10. self.base_input(page.login_username,username)
  11. # 输入密码
  12. def page_input_passwrod(self,pwd):
  13. self.base_input(page.login_pwd,pwd)
  14. # 输入验证码
  15. def page_input_verify_code(self,code):
  16. self.base_input(page.login_verify_code,code)
  17. # 点击登录
  18. def page_click_login_btn(self):
  19. self.base_click(page.login_btn)
  20. # 获取异常提示信息
  21. def page_get_error_info(self):
  22. return self.base_get_text(page.login_err_info)
  23. # 点击异常信息框 确定
  24. def page_click_error_btn_ok(self):
  25. self.base_click(page.login_err_btn_ok)
  26. # 截图
  27. def page_get_screenshot(self):
  28. self.base_get_image()
  29. # 组合业务方法
  30. def page_login(self,username,pwd,code):
  31. self.page_input_username(username)
  32. self.page_input_passwrod(pwd)
  33. self.page_input_verify_code(code)
  34. self.page_click_login_btn()

业务层:test_login.py

  1. """test_login.py"""
  2. # 导包
  3. import unittest
  4. from time import sleep
  5. from parameterized import parameterized
  6. from v3.base.get_driver import GetDriver
  7. from v3.page.page_login import PageLogin
  8. # 参数化
  9. def get_data():
  10. return [("13099999999","123456","8888","账号不存在!"),
  11. ("17864307785","error","8888","密码错误!")]
  12. # 新建测试类并继承
  13. class TestLogin(unittest.TestCase):
  14. # setUp
  15. @classmethod
  16. def setUpClass(cls):
  17. # 实例化driver并获取
  18. cls.driver = GetDriver().get_driver()
  19. # 实例化 获取登录对象
  20. cls.login =PageLogin(cls.driver)
  21. # 点击登录链接
  22. cls.login.page_click_login_link()
  23. # tearDown
  24. @classmethod
  25. def tearDownClass(cls):
  26. # 关闭driver浏览器驱动对象
  27. GetDriver().quit_driver()
  28. # 登录测试方法
  29. @parameterized.expand(get_data())
  30. def test_login(self,username,pwd,code,expect_result):
  31. # 调用登录方法
  32. self.login.page_login(username,pwd,code)
  33. # 获取登录提示信息
  34. result = self.login.page_get_error_info()
  35. print("result;",result)
  36. sleep(1)
  37. #断言
  38. try:
  39. self.assertIn(result,expect_result)
  40. sleep(1)
  41. except AssertionError:
  42. #截图
  43. self.login.page_get_screenshot()
  44. raise
  45. finally:
  46. # 点击提示框确定按钮
  47. self.login.page_click_error_btn_ok()
  48. sleep(1)

(六)V4版本(PO模式+数据驱动)

1 实现步骤

  1. 采用PO模式的分层思想对页面进行封装
  2. 编写测试脚本
  3. 使用参数化传入测试数据
  4. 把测试数据定义到JSON数据文件中

2 代码结构图

 å¨è¿éæå¥å¾çæè¿°

 3 代码

对象库层:base.py

  1. """base.py"""
  2. # -*-coding:utf-8 -*-
  3. # Auothor:yue_luo
  4. from selenium.webdriver.support.wait import WebDriverWait
  5. import time
  6. class Base:
  7. def __init__(self,driver):
  8. self.driver =driver
  9. def base_find_element(self,loc,timeout=30,poll=0.5):
  10. return WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll).until(lambda x:x.find_element(*loc))
  11. def base_click(self,loc):
  12. self.base_find_element(loc).click()
  13. # 获取value属性方法
  14. def base_get_value(self,loc):
  15. return self.base_find_element(loc).get_attribute("value")
  16. def base_get_img(self):
  17. self.driver.get_screenshot_as_file("./{}.png".format(time.strftime("%Y_%m_%d_%H_%M_%S")))
  1. """get_driver.py"""
  2. # -*-coding:utf-8 -*-
  3. # Auothor:yue_luo
  4. from selenium import webdriver
  5. from PO import page
  6. class GetDriver:
  7. driver = None
  8. @classmethod
  9. def get_driver(cls):
  10. if cls.driver == None:
  11. cls.driver = webdriver.Chrome()
  12. cls.driver.maximize_window()
  13. cls.driver.get(page.url)
  14. return cls.driver
  15. @classmethod
  16. def quit_driver(cls):
  17. if cls.driver:
  18. cls.driver.quit()
  19. """置空,一定要置空"""
  20. cls.driver = None

操作层:_ _init _ _.py 和 page_login.py

  1. """__init__.py"""
  2. # -*-coding:utf-8 -*-
  3. # Auothor:yue_luo
  4. from selenium.webdriver.common.by import By
  5. """以下为服务器域名配置地址"""
  6. url ="http://cal.apple886.com/"
  7. """以下为登录页面配置信息"""
  8. # cal_num = By.CSS_SELECTOR,"#simple"
  9. cal_add = By.CSS_SELECTOR,"#simpleAdd"
  10. cal_eq = By.CSS_SELECTOR,"#simpleEqual"
  11. cal_result = By.CSS_SELECTOR,"#resultIpt"
  12. cal_clear = By.CSS_SELECTOR,"#simpleClearAllBtn"
  1. """page_cal.py"""
  2. # -*-coding:utf-8 -*-
  3. # Auothor:yue_luo
  4. from time import sleep
  5. from PO import page
  6. from PO.base.base import Base
  7. from selenium.webdriver.common.by import By
  8. class PageCal(Base):
  9. def page_click_num(self,num):
  10. s = By.CSS_SELECTOR ,"#simple2"
  11. self.base_find_element(s)
  12. for n in str(num):
  13. loc = By.CSS_SELECTOR,"#simple{}".format(n)
  14. self.base_click(loc)
  15. def page_click_add(self):
  16. self.base_click(page.cal_add)
  17. def page_click_eq(self):
  18. self.base_click(page.cal_eq)
  19. def page_get_value(self):
  20. return self.base_get_value(page.cal_result)
  21. def page_click_clear(self):
  22. self.base_click(page.cal_clear)
  23. def page_get_img(self):
  24. self.base_get_img()
  25. def page_add_cal(self,a,b):
  26. self.page_click_num(a)
  27. sleep(0.5)
  28. self.page_click_add()
  29. self.page_click_num(b)
  30. sleep(0.5)
  31. self.page_click_eq()
  32. sleep(0.5)

 业务层:test_login.py

  1. """test_cal.py"""
  2. # -*-coding:utf-8 -*-
  3. # Auothor:yue_luo
  4. import unittest
  5. from PO.base.get_driver import GetDriver
  6. from PO.page.page_cal import PageCal
  7. from parameterized import parameterized
  8. from PO.tool.read_json import read_json
  9. def get_data():
  10. data = read_json("cal.json")
  11. # print(data)
  12. """
  13. 期望数据格式:[(1234, 1234, 2468), (1, 2, 3), (123445, 223265, 32626)]
  14. """
  15. list = []
  16. for n in data.values():
  17. list.append((n["a"], n["b"], n["expect_result"]))
  18. return list
  19. class TestCal(unittest.TestCase):
  20. # driver = None
  21. @classmethod
  22. def setUpClass(cls):
  23. cls.driver = GetDriver().get_driver()
  24. cls.cal = PageCal(cls.driver)
  25. @classmethod
  26. def tearDownClass(cls):
  27. GetDriver().quit_driver()
  28. @parameterized.expand(get_data())
  29. def test_add_cla(self,a,b,expect_result):
  30. self.cal.page_add_cal(a,b)
  31. result =self.cal.page_get_value()
  32. print("result:",result)
  33. print("ex_result:",expect_result)
  34. #136465446
  35. try:
  36. self.assertEqual(result,str(expect_result))
  37. except:
  38. self.cal.page_get_img()
  39. raise
  40. finally:
  41. self.cal.page_click_clear()

数据(cal.json)

  1. {
  2. "cal_001":{"a":1,"b":2,"expect_result":3},
  3. "cal_004":{"a":1,"b":22,"expect_result":23},
  4. "cal_005":{"a":1,"b":23,"expect_result":24},
  5. "cal_006":{"a":1,"b":42,"expect_result":43},
  6. "cal_008":{"a":1,"b":52,"expect_result":53},
  7. "cal_007":{"a":1,"b":522,"expect_result":523},
  8. "cal_002":{"a":123445,"b":223265,"expect_result":32626},
  9. "cal_003":{"a":1234,"b":1234,"expect_result":2468}
  10. }

工具类(read_json.py)

  1. import json
  2. def read_json(filename):
  3. filepath ="../data/"+ filename
  4. with open(filepath,"r",encoding="utf8") as f:
  5. return json.load(f)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/540110
推荐阅读
相关标签
  

闽ICP备14008679号