自动化测试(UI) 您所在的位置:网站首页 软件测试的坏处 自动化测试(UI)

自动化测试(UI)

2024-07-09 18:46| 来源: 网络整理| 查看: 265

前言

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版本 示例代码

""" 登录功能-账号不存在 """ from selenium import webdriver # 创建浏览器驱动对象,并完成初始化操作 driver = webdriver.Firefox() driver.maximize_window() driver.implicitly_wait(10) driver.get("http://localhost") # 点击首页的‘登录’链接,进入登录页面 # 输入用户名 driver.find_element_by_id("username").send_keys("11234567843") # 输入密码 driver.find_element_by_name("password").send_keys("23615115") # 点击登录按钮 driver.find_element_by_name("sbtbutton").click() # 获取提示信息 msg = driver.find_element_by_class_name(".layui-layer-padding").text print("msg=", msg) expect_result ="账号不存在!" try: # 断言 assert expect_result==msg print("result:",result) except AssertionError: # 截图 driver.get_screenshot_as_file("./{}.png".format(time.strftime("%Y_%m_%d_%H_%M_%S"))) print("result:",result) raise # 关闭驱动对象 driver.quit()

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

(四)V2版本

示例代码

# -*-coding:utf-8 -*- # Auothor:yue_luo import time import unittest from time import sleep from selenium import webdriver class TestLogin(unittest.TestCase): """ 对登录模块的功能进行测试 """ driver = None @classmethod def setUpClass(cls): cls.driver = webdriver.Chrome() cls.driver.maximize_window() cls.driver.implicitly_wait(10) cls.driver.get("http://www.tpshop.com") cls.driver.find_element_by_link_text("登录").click() @classmethod def tearDownClass(cls): cls.driver.quit() # 账号不存在 def test_login_username_is_error(self): # 输入用户名 username =self.driver.find_element_by_id("username") username.clear() username.send_keys("13099999999") # 输入密码 password=self.driver.find_element_by_id("password") password.clear() password.send_keys("123456") # 输入验证码 verify_code = self.driver.find_element_by_id("verify_code") verify_code.clear() verify_code.send_keys("8888") # 点击‘登录’ self.driver.find_element_by_name("sbtbutton").click() # 断言提示信息 msg = self.driver.find_element_by_class_name("layui-layer-content").text print("msg=", msg) try: self.assertIn("账号不存在!", msg) sleep(3) except AssertionError: self.driver.get_screenshot_as_file("./{}.png".format(time.strftime("%Y_%m_%d_%H_%M_%S"))) raise finally: self.driver.find_element_by_css_selector(".layui-layer-btn0").click() def test_login_password_is_error(self): # 输入用户名 username = self.driver.find_element_by_id("username") username.clear() username.send_keys("17864307785") # 输入密码 password = self.driver.find_element_by_id("password") password.clear() password.send_keys("error") # 输入验证码 verify_code = self.driver.find_element_by_id("verify_code") verify_code.clear() verify_code.send_keys("8888") # 点击‘登录’ self.driver.find_element_by_name("sbtbutton").click() # 断言提示信息 msg = self.driver.find_element_by_class_name("layui-layer-content").text print("msg=", msg) try: self.assertIn("密码错误!", msg) sleep(3) except AssertionError: self.driver.get_screenshot_as_file("./{}.png".format(time.strftime("%Y_%m_%d_%H_%M_%S"))) raise finally: self.driver.find_element_by_css_selector(".layui-layer-btn0").click()

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

存在的问题 代码冗余

(五)V3版本(PO模式)

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

示例代码

对象库层:base.py

"""base.py""" import time from selenium import webdriver from selenium.webdriver.support.wait import WebDriverWait class Base: # 初始化 def __init__(self,driver): self.driver = driver # 查找元素方法 def base_find_element(self,loc,timeout=30,poll_frequency=0.5): return WebDriverWait(driver=self.driver,timeout=timeout,poll_frequency=poll_frequency).until(lambda x:x.find_element(*loc)) # 点击方法 def base_click(self,loc): self.base_find_element(loc).click() # 输入方法 def base_input(self,loc,value): element = self.base_find_element(loc) element.clear() element.send_keys(value) # 获取文本方法 def base_get_text(self,loc): msg = self.base_find_element(loc).text return msg # 截图 def base_get_image(self,): self.driver.get_screenshot_as_file("./{}.png".format(time.strftime("%Y_%m_%d_%H_%M_%S")))

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

"""__init__.py""" # -*-coding:utf-8 -*- # Auothor:yue_luo from selenium.webdriver.common.by import By """以下为服务器域名配置地址""" url ="http://www.tpshop.com" """以下为登录页面配置信息""" # 登录链接 login_link = By.PARTIAL_LINK_TEXT , "登录" # 用户名 login_username = By.ID,"username" # 密码 login_pwd = By.NAME,"password" # 验证码 login_verify_code = By.CSS_SELECTOR,"#verify_code" # 登录按钮 login_btn =By.NAME,"sbtbutton" # 获取异常文本信息 login_err_info = By.CLASS_NAME,"layui-layer-content" # 异常提示框 确定按钮 login_err_btn_ok = By.CSS_SELECTOR,".layui-layer-btn0" """page_login.py""" from v3 import page from v3.base.base import Base class PageLogin(Base): # 点击登录链接 def page_click_login_link(self): self.base_click(page.login_link) # 输入用户名 def page_input_username(self,username): self.base_input(page.login_username,username) # 输入密码 def page_input_passwrod(self,pwd): self.base_input(page.login_pwd,pwd) # 输入验证码 def page_input_verify_code(self,code): self.base_input(page.login_verify_code,code) # 点击登录 def page_click_login_btn(self): self.base_click(page.login_btn) # 获取异常提示信息 def page_get_error_info(self): return self.base_get_text(page.login_err_info) # 点击异常信息框 确定 def page_click_error_btn_ok(self): self.base_click(page.login_err_btn_ok) # 截图 def page_get_screenshot(self): self.base_get_image() # 组合业务方法 def page_login(self,username,pwd,code): self.page_input_username(username) self.page_input_passwrod(pwd) self.page_input_verify_code(code) self.page_click_login_btn()

业务层:test_login.py

"""test_login.py""" # 导包 import unittest from time import sleep from parameterized import parameterized from v3.base.get_driver import GetDriver from v3.page.page_login import PageLogin # 参数化 def get_data(): return [("13099999999","123456","8888","账号不存在!"), ("17864307785","error","8888","密码错误!")] # 新建测试类并继承 class TestLogin(unittest.TestCase): # setUp @classmethod def setUpClass(cls): # 实例化driver并获取 cls.driver = GetDriver().get_driver() # 实例化 获取登录对象 cls.login =PageLogin(cls.driver) # 点击登录链接 cls.login.page_click_login_link() # tearDown @classmethod def tearDownClass(cls): # 关闭driver浏览器驱动对象 GetDriver().quit_driver() # 登录测试方法 @parameterized.expand(get_data()) def test_login(self,username,pwd,code,expect_result): # 调用登录方法 self.login.page_login(username,pwd,code) # 获取登录提示信息 result = self.login.page_get_error_info() print("result;",result) sleep(1) #断言 try: self.assertIn(result,expect_result) sleep(1) except AssertionError: #截图 self.login.page_get_screenshot() raise finally: # 点击提示框确定按钮 self.login.page_click_error_btn_ok() sleep(1)

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

1 实现步骤

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

2 代码结构图

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

 3 代码

对象库层:base.py

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

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

"""__init__.py""" # -*-coding:utf-8 -*- # Auothor:yue_luo from selenium.webdriver.common.by import By """以下为服务器域名配置地址""" url ="http://cal.apple886.com/" """以下为登录页面配置信息""" # cal_num = By.CSS_SELECTOR,"#simple" cal_add = By.CSS_SELECTOR,"#simpleAdd" cal_eq = By.CSS_SELECTOR,"#simpleEqual" cal_result = By.CSS_SELECTOR,"#resultIpt" cal_clear = By.CSS_SELECTOR,"#simpleClearAllBtn" """page_cal.py""" # -*-coding:utf-8 -*- # Auothor:yue_luo from time import sleep from PO import page from PO.base.base import Base from selenium.webdriver.common.by import By class PageCal(Base): def page_click_num(self,num): s = By.CSS_SELECTOR ,"#simple2" self.base_find_element(s) for n in str(num): loc = By.CSS_SELECTOR,"#simple{}".format(n) self.base_click(loc) def page_click_add(self): self.base_click(page.cal_add) def page_click_eq(self): self.base_click(page.cal_eq) def page_get_value(self): return self.base_get_value(page.cal_result) def page_click_clear(self): self.base_click(page.cal_clear) def page_get_img(self): self.base_get_img() def page_add_cal(self,a,b): self.page_click_num(a) sleep(0.5) self.page_click_add() self.page_click_num(b) sleep(0.5) self.page_click_eq() sleep(0.5)

 业务层:test_login.py

"""test_cal.py""" # -*-coding:utf-8 -*- # Auothor:yue_luo import unittest from PO.base.get_driver import GetDriver from PO.page.page_cal import PageCal from parameterized import parameterized from PO.tool.read_json import read_json def get_data(): data = read_json("cal.json") # print(data) """ 期望数据格式:[(1234, 1234, 2468), (1, 2, 3), (123445, 223265, 32626)] """ list = [] for n in data.values(): list.append((n["a"], n["b"], n["expect_result"])) return list class TestCal(unittest.TestCase): # driver = None @classmethod def setUpClass(cls): cls.driver = GetDriver().get_driver() cls.cal = PageCal(cls.driver) @classmethod def tearDownClass(cls): GetDriver().quit_driver() @parameterized.expand(get_data()) def test_add_cla(self,a,b,expect_result): self.cal.page_add_cal(a,b) result =self.cal.page_get_value() print("result:",result) print("ex_result:",expect_result) #136465446 try: self.assertEqual(result,str(expect_result)) except: self.cal.page_get_img() raise finally: self.cal.page_click_clear()

数据(cal.json)

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

工具类(read_json.py)

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



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有