1.创建工具类utils.py文件
# 工具类
import time
from selenium import webdriver
# 定义浏览器驱动获取和关闭的类
# 减少多个用例都调用的实例化浏览器驱动以及最大化和隐式等待的方法,减少部分代码冗余
class DriverUtil:
# 私有变量,用来存储浏览器驱动对象
__driver = None
is_open = True
# 获取浏览器驱动
# 1.用类级别定义方法的目的是为了方便测试用例的代码的调用
# 2.为了防止测试用例里面多次调用获取浏览器驱动的方法,导致多个浏览器驱动,加上判断浏览器是否为空的判断
@classmethod
def get_driver(cls):
if cls.__driver is None:
cls.__driver = webdriver.Chrome()
cls.__driver.maximize_window()
cls.__driver.implicitly_wait(10)
return cls.__driver
# 关闭浏览器驱动
# 1.增加判断浏览器驱动是否为空的判断,如果为空,则不执行关闭
@classmethod
def quit_dirver(cls):
if cls.is_open and cls.__driver is not None:
time.sleep(2)
cls.get_driver().quit()
cls.__driver = None
# 窗口切换的方法
def switch_window():
driver = DriverUtil.get_driver()
handlers = driver.window_handles
driver.switch_to.window(handlers[-1])
2.创建公共的config.py文件
import os
import logging.handlers
# os.path.abspath(__file__) 获取当前文件完整路径的方法
# os.path.dirname(os.path.abspath(__file__))获取当前文件完整路径的目录
base_DIR = os.path.dirname(os.path.abspath(__file__))
# 日志配置方法
def log_basic():
# 1.创建日志器,项目中创建日志器不要定义名称,默认为root
logger = logging.getLogger()
# 2.设置日志级别
logger.setLevel(level=logging.INFO)
# 3.创建处理器
ls = logging.StreamHandler() # 打印到控制台的处理器
file_name = base_DIR + '/log/test.log'
lht = logging.handlers.TimedRotatingFileHandler(filename=file_name, when='midnight', interval=1,backupCount=2) # 按时间分隔日志文件的处理器
# 4.创建格式化器
fmt_str = "%(asctime)s %(levelname)s [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s"
fomatter = logging.Formatter(fmt=fmt_str)
# 5.将格式化器添加到处理器
ls.setFormatter(fomatter)
lht.setFormatter(fomatter)
# 6.将处理器添加到日志器
logger.addHandler(ls)
logger.addHandler(lht)
3.创建基类文件;base:base_page.py
# 基类文件
from utils import DriverUtil
# 创建对象库层的基类
class basePage:
def __init__(self):
self.driver = DriverUtil.get_driver()
# 定位元素
# 抽取共性的代码
# 基类的方法最终还是提供给这些共性的代码来使用
def find_element(self, location):
return self.driver.find_element(*location)
# 创建操作层的基类
class baseHandler:
# 输入文本
def input_text(self, element, text):
# 清空默认用户名输入框的信息
# 1.元素对象在po的对象库层可以获取到
# 2.共性的方法是供子类调用
element.clear()
element.send_keys(text)
4.进行PO分层;主要分为对象库层、操作层、业务层;Page:login_page.py
# PO分层文件
from selenium.webdriver.common.by import By
from base.base_page import basePage, baseHandler
# 登录页面对象库层
class LoginPage(basePage):
# 1.定义初始化方法,初始化方法需要先定义好界面需要操作的元素对象
# 2.实现获取这些定义好的元素的方法
def __init__(self):
# 重写父类的初始化方法,其实就是获取浏览器驱动对象
super().__init__()
# 登录按钮
self.submit_btn = (By.XPATH, "//span[text()='登录']")
# 用户名输入框
self.username = (By.XPATH, "//input[@placeholder='请输入用户名或邮箱']")
# 密码输入框
self.password = (By.XPATH, "//input[@placeholder='请输入密码']")
# 立即登录按钮
self.login_btn = (By.XPATH, "//span[text()='立即登录']")
#获取登录按钮的元素对象
def find_submit_btn(self):
return self.find_element(self.submit_btn)
# 获取用户名输入框的元素对象
def find_username(self):
return self.find_element(self.username)
# 获取密码输入框的元素对象
def find_password(self):
return self.find_element(self.password)
# 获取立即登录的元素对象
def find_login_btn(self):
return self.find_element(self.login_btn)
# 操作层
# 操作层需要对页面的元素进行操作,就需要获取的元素对象,直接调用对象库层的查找元素对象的方法
class LoginHandler(baseHandler):
# 定义初始化方法,用来实例化对象库层类
def __init__(self):
self.login_page = LoginPage()
#点击登录
def click_submit_btn(self):
self.login_page.find_submit_btn().click()
# 输入用户名
def input_username(self, username):
# 清空默认用户名输入框的信息
self.input_text(self.login_page.find_username(), username)
# 输入密码
def input_password(self, pwd):
# 清空默认密码输入框的信息
self.input_text(self.login_page.find_password(), pwd)
# 点击立即登录按钮
def click_login_btn(self):
self.login_page.find_login_btn().click()
# 业务层
# 目的是用来组装业务流程
class LoginProxy:
def __init__(self):
self.login_handler = LoginHandler()
# 登录业务完整操作
def login(self, username, pwd):
#点击登录
self.login_handler.click_submit_btn()
# 输入用户名
self.login_handler.input_username(username)
# 输入密码
self.login_handler.input_password(pwd)
# 点击立即登录
self.login_handler.click_login_btn()
5.用例;data:test_login.json
{
"account_not_exist":{
"username":"yiqyiq",
"password":"Yiq123456",
"expect":"首页"
}
}
6.使用unittest框架实现测试用例;case:test_login.py
import time
import unittest
import parameterized as parameterized
from config import base_DIR
from page.login_page import LoginProxy
from utils import DriverUtil
import json
def build_data():
test_data = []
# 1.通过方法或者函数读取json数据
with open(base_DIR + '/data/test_login.json', encoding='utf-8')as f:
json_data = json.load(f)
# 2.将读取回来的数据组装成parameterized所要求的数据格式
for case_data in json_data.values():
test_data.append((case_data.get('username'),
case_data.get('password'),
case_data.get('expect')))
print(test_data)
return test_data
class TestLogin(unittest.TestCase):
@classmethod
def setUpClass(cls):
# 创建浏览器驱动,并且完成初始化操作
cls.driver = DriverUtil.get_driver()
cls.login_proxy = LoginProxy()
@classmethod
def tearDownClass(cls):
# 关闭浏览器
time.sleep(2)
DriverUtil.quit_dirver()
def setUp(self):
# 方法级别的fixture,由于每条测试都要回归到原点
self.driver.get('https://ad.xxx.com.cn/home')
# parameterized中的数据是一个数组,数组内的每个元素可以数组或者元组
@parameterized.expand(build_data)
def test_login(self, username, password, expect):
"""
登录功能-登录成功
"""
self.login_proxy.login(username, password)
# 获取提示信息
time.sleep(3)
msg = self.driver.title
self.assertIn(expect, msg)
7.使用测试套件运行测试用例,并生成HTML格式的测试报告;run_suitte.py
# TestLoader
# 导包
import time
import unittest
from HTMLTestRunner import HTMLTestRunner
from config import base_DIR
# 生成测试套件
suite = unittest.TestLoader().discover('./case', 'test*')
# 设置浏览器驱动关闭开关为False
# DriverUtil.is_open = False
# 实例化运行器
#runner = unittest.TextTestRunner()
# 运行测试用例
#runner.run(suite)
# 定义测试报告存放路径
report_path = base_DIR + "/report/report-{}.html".format(time.strftime("%Y%m%d%H%M%S"))
with open(report_path, "wb") as f:
runner = HTMLTestRunner(stream=f, title='测试报告', description="Chrome")
runner.run(suite)
# 打开关闭浏览器开关,并关闭浏览器
# DriverUtil.is_open = True
# DriverUtil.quit_dirver()
此时的目录结构如下:
UITest -base --__init__.py --base_page.py -case --__init__.py --test_login.py -data --test_login.json -page --__init__.py --login_page.py -report -config.py -run_suite.py -utils.py



