PO模式
- PO是什么?
PO:Page Object(页面对象),将自动化涉及的页面或模块封装成对象。
- PO能解决什么问题?
1、代码复用性
2、便于维护(脚本层与业务分离)--如果元素信息发生变化了,也不用去修改脚本。
PO如何做?
- Base层
存放所有页面公共方法
- Page层
基于页面或模块单独封装当前页面要操作的对象
- Script层
脚本层+unittest
非PO模式代码实现
import unittest from selenium import webdriver from selenium.webdriver.common.by import By class TestLogin(unittest.TestCase): def setUp(self) -> None: self.driver=webdriver.Chrome() self.driver.maximize_window() self.driver.get("https://hmshop-test.itheima.net/Home/user/login.html") self.driver.implicitly_wait(10) def tearDown(self) -> None: sleep(5) self.driver.quit() def test01_login(self): #输入用户名 self.driver.find_element(By.CSS_SELECTOR,"#username").send_keys("16612345678") #输入密码 self.driver.find_element(By.CSS_SELECTOR, "#password").send_keys("123456") #输入验证密码 self.driver.find_element(By.CSS_SELECTOR, "#verify_code").send_keys("8888") #点击登录 self.driver.find_element(By.CSS_SELECTOR, ".J-login-submit").click()
PO设计
- 结构
- base:
存放所有Page页面公共方法
- page:
将页面封装为对象
- script:
测试脚本
- base:
案例(登录)
- base:
分析要实现页面公共方法
"""
Base类:存放所有Page页面公共操作方法!
"""
from selenium.webdriver.support.wait import WebDriverWait
class Base:
def __init__(self, driver):
self.driver = driver
# 查找元素
# 输入方法
# 点击方法
# 获取文本值方法
- base实现
"""Base类:存放所有Page页面公共操作方法"""
from selenium.webdriver.support.wait import WebDriverWait
class Base:
def __init__(self,driver):
self.driver=driver
#查找方法
def base_find(self,loc,timeout=10,poll_frequency=0.5):
#显示等待 ->查找元素 loc=(By.ID,"userA") *loc=loc[0],loc[1]
return WebDriverWait(self.driver,timeout,poll_frequency).until(lambda x:x.find_element(loc[0],loc[1]))
#输入方法
def base_input(self,loc,value):
#获取元素
el =self.base_find(loc)
#清空操作
el.clear()
# 输入内容
el.send_keys(value)
#点击方法
def base_click(self,loc):
self.base_find(loc).click()
#获取文本值方法
def base_get_text(self,loc):
return self.base_find(loc).text
- page结构搭建
"""
模块名:page_模块单词
类名:大驼峰将模块移植进来,去掉下划线和数字
方法:自动化测试当前页面要操作哪些元素,就封装哪些元素
"""
class PageLogin:
#输入用户名
def __page_username(self):
pass
#输入密码
def __page_pwd(self):
pass
#输入验证码
def __page_verify_code(self):
pass
#点击登录按钮
def __page_click_login_btn(self):
pass
#获取昵称
def page_get_nickname(self):
pass
# 组合业务方法 (强调:测试业务成调用此方法,便捷。)
def page_login(self):
pass
- 配置信息整理
# 验证码
verify_code = By.CSS_SELECTOR, "#verify_code"
# 登录按钮
login_btn = By.CSS_SELECTOR, ".J-login-submit"
# 昵称
nick_name = By.CSS_SELECTOR, ".userinfo"
# 提示:nick_name = By.CLASS_NAME, ".userinfo" == (By.CLASS_NAME,".userinfo")
- page实现
"""
模块名:page_模块单词
类名:大驼峰将模块移植进来,去掉下划线和数字
方法:自动化测试当前页面要操作哪些元素,就封装哪些元素
"""
from selenium.webdriver.common.by import By
from base.base import Base
#用户名
username = (By.CSS_SELECTOR,"#username")
#密码
pwd = By.CSS_SELECTOR,"#password"
#验证码
verify_code = By.CSS_SELECTOR,"#verify_code"
#登录按钮
login_btn=By.CSS_SELECTOR,".J-login-submit"
#昵称
nick_name=By.CSS_SELECTOR,".userinfo"
class PageLogin(Base):
#输入用户名
def __page_username(self,value):
self.base_input(username,value)
#输入密码
def __page_pwd(self, value):
self.base_input(pwd, value)
#输入验证码
def __page_verify_code(self, value):
self.base_input(verify_code, value)
#点击登录按钮
def __page_click_login_btn(self):
self.base_click(login_btn)
#获取昵称
def page_get_nickname(self):
return self.base_get_text(nick_name)
# 组合业务方法 (强调:测试业务成调用此方法,便捷。)
def page_login(self,phone,password,code):
self.__page_username(phone)
self.__page_pwd(password)
self.__page_verify_code(code)
self.__page_click_login_btn()
- script实现
import unittest
from selenium import webdriver
from page.page_login import PageLogin
class TestLogin(unittest.TestCase):
def setUp(self) -> None:
self.driver=webdriver.Chrome()
self.driver.maximize_window()
self.driver.get("https://hmshop-test.itheima.net/Home/user/login.html")
self.login =PageLogin(self.driver)
def tearDown(self) -> None:
self.driver.quit()
def test01_login(self,phone="13012345678 ",password="123456",code="8888"):
#调用登录业务
self.login.page_login(phone,password,code)
#断言
nickname=self.login.page_get_nickname()
print("nickname",nickname)
数据驱动(parameterized)
理论回顾
- 什么是数据驱动?
以测试数据驱动用例执行(测试数据和代码分离)
- 为什么要数据驱动?
便于维护(维护的焦点从代码转到测试数据)
- 数据如何操作?
1、编写数据存储文件
2、编程读取数据工具
3、使用参数化引用
实际操作
- 步骤1、编写数据存储文件
口诀:
1、新建json文件,在文件中编写一个{}
2、有几个模块,写几个key,值为列表
3、列表中参数化数据有几组,就写几个{}
3、每个{}中组成->说明+参数数据+预期结果
- login.json
{
"login": [
{
"desc": "登录成功",
"phone": "13012345678",
"password": "123456",
"code": "8888",
"expect_text": "13012345678"
}
]
}
- 步骤2、读取工具封装(util.py)
import json
import os
#读取json工具
def read_json(filename,key):
filepath = os.path.dirname(__file__)+os.sep+"data"+os.sep+filename
arr=[]
with open(filepath,"r",encoding="utf-8") as f:
for data in json.load(f).get(key):
arr.append(tuple(data.values())[1:])
return arr
if __name__ == '__main__':
print(read_json("login.json","login"))
- 步骤3、参数化引用
import unittest
from time import sleep
from selenium import webdriver
from page.page_login import PageLogin
from parameterized import parameterized
from util import read_json
class TestLogin(unittest.TestCase):
def setUp(self) -> None:
self.driver=webdriver.Chrome()
self.driver.maximize_window()
self.driver.get("https://hmshop-test.itheima.net/Home/user/login.html")
self.login =PageLogin(self.driver)
def tearDown(self) -> None:
sleep(3)
self.driver.quit()
@parameterized.expand(read_json("login.json","login"))
def test01_login(self,phone,password,code,expect_text):
try:
#调用登录业务
self.login.page_login(phone,password,code)
#断言
nickname=self.login.page_get_nickname()
print("nickname",nickname,expect_text)
except Exception as e:
print("错误!",e)