Python+Selenium+Pytest+PO+Allure+DDT实现Web UI自动化测试
前言
Python:编程语言
Pytest](https://so.youkuaiyun.com/so/search?q=pytest&spm=1001.2101.3001.7020):独立的、全功能的Python单元测试框架
Selenium:用于Web应用程序测试的工具,测试框架
Allure:测试报告
DDT:数据驱动
一、前置条件
1. 安装Python开发环境
1.1 Python解释器
3.8.0版本
1.2 Pycharm集成开发环境
专业版
2.下载览器驱动
下载浏览器驱动,浏览器驱动版本要与浏览器版本一致。
下载地址:
Chrome:http://npm.taobao.org/mirrors/chromedriver/
Firefox:https://github.com/mozilla/geckodriver/releases
Edge:https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/
Safari:https://webkit.org/blog/6900/webdriver-support-in-safari-10/
- 浏览器驱动加到python环境变量中: path
- 或者chromedriver.exe与python.exe放在同一个目录
- 运行时也可以指定Chromedriver的路径
本项目中将驱动chromedriver.exe放到工程的driver目录下-固定目录。
注:MCX调度台 查看浏览器驱动的版本
1、ctrl + F12 调出element窗口
2、点击Console 输入 'navigator.userAgent' 注意区分大小写
3.安装allure
1)下载allure,下载网址 https://github.com/allure-framework/allure2/releases
2)解压到电脑本地(此目录后续使用,不要动),配置环境变量(把allure目录下的bin目录配置到系统环境变量中的path路径)
参考博客《allure报告框架介绍》https://blog.youkuaiyun.com/weixin_59868574/article/details/135502984。
- 下载第三方库
工程新建requirements.txt,输入以下内容,之后安装requirements.txt文件,或单个安装
pytest== 7.4.2
selenium== 3.141.0
urllib3==1.26.18
PySocks~=1.7.1
PyYAML~=6.0.1
psutil~=5.9.7
allure-pytest~=2.13.2
二、测试框架整体目录
三、测试工具类utils
3.1 管理时间timer.py
包括时间戳,日期等其他模块会使用的字符串,将时间封装成一个单独的模块,让其他模块来调用。
在 utils目录新建timer.py模块
"""
@File : timer.py
@Author : TJC
@Date : 2023/7/10
@Desc :时间字符串管理
"""
import time
import datetime
from functools import wraps
def timestamp():
"""时间戳"""
return time.time()
def dt_strftime(fmt="%Y%m"):
"""
datetime格式化时间
:param fmt “%Y%m%d %H%M%S
"""
return datetime.datetime.now().strftime(fmt)
def sleep(seconds=1.0):
"""睡眠时间"""
time.sleep(seconds)
def running_time(func):
"""函数运行时间"""
@wraps(func)
def wrapper(*args, **kwargs):
start = timestamp()
res = func(*args, **kwargs)
print("校验元素done!用时%.3f秒!" % (timestamp() - start))
return res
return wrapper
if __name__ == '__main__':
print(dt_strftime("%Y%m%d %H%M%S"))
3.2 日志管理logger.py
测试脚本需要记录运行日志,所以将日志管理封装成一个模块,用于被其他模块调用。
在utils目录中新建logger.py模块,代码如下:
"""
@File : logger.py
@Author : TJC
@Date : 2023/7/10
@Desc :日志管理模块
"""
import logging
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
from config.config import cm
class Log:
def __init__(self, seg):
self.seg = seg
# 创建logger
self.logger = logging.getLogger()
if not self.logger.handlers:
self.logger.setLevel(logging.DEBUG)
# 定义日志文件大小为10M
max_bytes = 10 * 1024 * 1024
# 定义保留的文件数量为50个
backup_count = 50
# 创建日志文件处理器,备份文件命名规则默认为添加后缀数字,数字越大时间越早
if self.seg == "time":
fh = TimedRotatingFileHandler(cm.log_file, when='D', backupCount=backup_count, encoding='utf-8')
"""
:param:when
'S':每秒切分日志文件
'M':每分钟切分日志文件
'H':每小时切分日志文件
'D':每天切分日志文件(默认值)
'W0' ~ 'W6':每星期切分日志文件(0表示星期一,1表示星期二,以此类推)
'midnight':每天午夜切分日志文件 与'D'相同
:param:interval 默认1,1D就是一天
"""
fh.setLevel(logging.INFO)
else:
fh = RotatingFileHandler(cm.log_file, maxBytes=max_bytes, backupCount=backup_count, encoding='utf-8')
fh.setLevel(logging.INFO)
# 创建一个handle输出到控制台
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
# 定义输出的格式
formatter = logging.Formatter(self.fmt)
fh.setFormatter(formatter)
ch.setFormatter(formatter)keyi
# 添加到handle
self.logger.addHandler(fh)
self.logger.addHandler(ch)
@property
def fmt(self):
return '%(levelname)s\t%(asctime)s\t[%(filename)s:%(lineno)d]\t%(message)s'
# 使用时间分割log文件,可以修改when参数,建议在性能测试脚本中使用
# log = Log('time').logger
# 使用大小分割log文件
log = Log('size').logger
四、配置文件夹config
4.1 公共配置config.py
将所有的文件目录,文件位置,封装后被其他模块调用,如果新增文件或新增目录,在这里添加方法。
在config目录下新建模块config.py,代码如下:
"""
@File : config.py
@Author : TJC
@Date : 2023/7/10
@Desc :工程的文件目录,文件位置
"""
import os
from utils.timer import dt_strftime
class ConfigManager:
"""
管理项目目录,文件
"""
DIR_CURR = os.path.abspath(__file__)
DIR_CONF = os.path.dirname(os.path.abspath(__file__))
# 项目路径
DIR_BASE = os.path.dirname(DIR_CONF)
DIR_COMM = os.path.join(DIR_BASE, "common")
DIR_UTIL = os.path.join(DIR_BASE, "utils")
# 页面元素和页面对象路径
DIR_PAGE = os.path.join(DIR_BASE, 'page_object')
# 页面元素
DIR_ELEMENT = os.path.join(DIR_BASE, "page_element")
DIR_DRIVER = os.path.join(DIR_BASE, "driver")
@property
def web_ini_file(self):
""" web 配置文件"""
ini_file = os.path.join(self.DIR_CONF, 'web_cfg.ini')
if not os.path.exists(ini_file):
raise FileNotFoundError("配置文件%s不存在!" % ini_file)
return ini_file
@property
def dir_report_json(self):
"""allure报告目录 json文件"""
report_dir = os.path.join(self.DIR_BASE, 'report')
os.makedirs(report_dir, exist_ok=True)
json_dir = os.path.join(report_dir, 'json')
os.makedirs(json_dir, exist_ok=True)
return json_dir
@property
def dir_report_html(self):
"""allure报告目录 html文件"""
report_dir = os.path.join(self.DIR_BASE, 'report')
os.makedirs(report_dir, exist_ok=True)
html_dir = os.path.join(report_dir, 'html')
os.makedirs(html_dir, exist_ok=True)
return html_dir
@property
def dir_log(self):
"""日志目录"""
log_dir = os.path.join(self.DIR_BASE, 'logs')
os.makedirs(log_dir, exist_ok=True)
return log_dir
@property
def log_file(self):
"""日志文件"""
return os.path.join(self.dir_log, '{}.log'.format(dt_strftime()))
@property
def dir_img(self):
"""截图目录"""
img_dir = os.path.join(self.dir_log, 'images')
os.makedirs(img_dir, exist_ok=True)
re