一、项目要求
1.1 项目简介
计算器是一个经典的编程场景,可以获取两个数据的计算结果。
1.1.1 知识点
- 变量定义规则
- 运算符
- 控制流 - 判断
1.1.2 受众
- 初级测试工程师
1.1.3 作业内容
- 实现相加函数:
- 计算范围为 -99 到 99 的整数和浮点数。
- 方法需要传递两个参数 a 和 b。
- 如果参数超出范围,打印提示语,返回提示信息。
- 传入在范围内的参数,方法返回 a 与 b 相加的结果。
1.2 实现过程
# 定义一个加法
def add(a, b):
'''
加法
:param a: 加数
:param b: 被加数
:return: 相加结果,或提示信息
'''
# 判断参数 a 是否整数、浮点数
if not (isinstance(a, int) or isinstance(a, float)):
# 类型错误,则返回提示信息
return "您输入的加数不是整数或浮点数,请重新输入。"
# 判断参数 b 是否整数、浮点数
if not (isinstance(b, int) or isinstance(b, float)):
# 类型错误,则返回提示信息
return "您输入的被加数不是整数或浮点数,请重新输入。"
# 判断参数 a 是否符合指定范围
if not (-99 <= a <= 99):
# 不符合,则返回提示信息
return "您输入的加数超出范围,请输入[-99,99]之间的数字。"
# 判断参数 b 是否符合指定范围
if not (-99 <= b <= 99):
# 不符合,则返回提示信息
return "您输入的被加数超出范围,请输入[-99,99]之间的数字。"
# 输入正常,则返回相加结果
return a + b
# 测试数据列表
test_cases = [
(50, 30), # 2个整数
(77.5, -8.31), # 2个浮点数
(-100, 50), # a 超出范围
(50, 100), # b 超出范围
("50", 30), # a 不是整数或浮点数
(50, "thirty"), # b 不是整数或浮点数
(0, 0), # 边界情况,零值相加
(-99, 99), # 边界情况,负数与正数相加
]
# 遍历测试数据, enumerate 函数同时获取索引和值
for i, (a, b) in enumerate(test_cases):
# i是当前循环的所有,从0开始
result = add(a, b)
# 调用 add 函数计算结果,将返回值存储在 result 变量中
print(f"测试用例 {i+1}: add({a},{b}) = {result}")
1.3 实现效果:
二、项目要求
2.1 项目简介
计算器是一个经典的编程场景,可以获取两个数据的计算结果。
2.1.1 知识点
- Pytest 测试框架基本用法
2.1.2 受众
- 初级测试工程师
2.1.3 作业内容
- 使用 Pytest 编写自动化测试用例对相加函数进行测试
- 在调用每个测试函数之前打印【开始计算】
- 在调用测试函数之后打印【结束计算】
- 调用完所有的测试用例最终输出【结束测试】
- 使用参数化实现测试数据的动态传递
2.1.4 被测源码
def is_match_condition(a):
'''
判断输入参数是否满足条件
:param a: 输入参数
:return: 满足条件返回 "符合条件",否则返回对应场景提示信息
'''
# 判断 a 的类型
if not (isinstance(a, int) or isinstance(a, float)):
return "参数为非数字"
# 判断 a 的范围
if a > 99 or a < -99:
return "参数大小超出范围"
return "符合条件"
def add(a, b):
'''
相加方法
:param a: 加数
:param b: 被加数
:return: a + b 的结果,或者提示信息
'''
# 判断 a 参数类型与范围符合条件
if is_match_condition(a) == "符合条件":
# 判断 b 参数类型与范围符合条件
if is_match_condition(b) == "符合条件":
return a + b
# b 不符合条件,返回提示信息
else:
return f"b{is_match_condition(b)}"
# a 不符合条件,返回提示信息
else:
return f"a{is_match_condition(a)}"
2.2 实现过程
新建test_calculator.py文件,调用被测源码的calculator.py
import pytest
from calculator import add
@pytest.fixture(autouse=True)
def start_end_add():
print("=====开始计算=====")
yield
print("=====结束计算=====")
def end_test():
print("=========结束测试=========")
@pytest.mark.parametrize(
"a, b, expected",
[
(50, 30, 80), # 正常情况
(-100, 50, "a参数大小超出范围"), # a 超出范围
(50, 100, "b参数大小超出范围"), # b 超出范围
(23.55, 55.13, 78.68), # 浮点数情况
("50", 30, "a参数为非数字"), # a 不是数字
(50, "thirty", "b参数为非数字"), # b 不是数字
(0, 0, 0), # 边界情况,零值相加
(-99, 99, 0), # 边界情况,负数与正数相加
]
)
def test_add(a, b, expected):
result = add(a, b)
assert result == expected
2.3 实现效果
三、项目要求
3.1 项目简介
计算器是近代人发明的可以进行数字运算的机器。 计算器通过对加法、减法、乘法、除法等功能的运算,将正确的结果展示在屏幕上。 可帮助人们更方便的实现数字运算。一般情况下计算器除显示计算结果外,还常有溢出指示、错误指示等功能。
3.1.1 知识点
- 测试流程与需求分析
- bug 提交与管理
- Pytest 测试框架基本用法
- 参数化
- 异常处理
- 标签、跳过用例
- 结合 Allure 生成测试报告与项目总结
- 数据驱动
- pytest fixture 实现测试装置及参数化
- pytest conftest.py 的用法
- pytest 文件配置 pytest.ini
- 使用第三方插件控制用例的执行顺序,分布式并发执行
- 使用分层思想,实现框架的合理构建
- 了解内置插件 hook 体系,实现插件开发
3.1.2 受众
- 资深测试工程师
3.1.3 作业内容
- 完整的测试流程,包含需求分析、测试计划设计、测试用例编写、测试执行、bug 的提交与管理。
- 使用思维导图完成需求分分析;提供完整测试计划模板,完成测试计划设计;应用多种测试用例设计方法,包括:等价类、边界值、错误推测法等。
- 测试执行过程中应用多种测试方法完成计算器的加法、除法运算。
- 结合项目管理工具完成 bug 的提交与管理,进行测试报告编写与项目总结。
- 编写自动化测试用例,结合 Allure 与截图技术等自动生成带截图与操作步骤的测试报告。
- 使用参数化减少代码量,提高代码的可维护性。
- 使用 mark 标签为测试用例分类
- 设置跳过、预期失败用例
- 对异常用例进行处理
- 掌握 Pytest 常用的装饰器,例如:添加标签、参数化、Fixture 等。
- 掌握 Pytest 自动化测试框架多种复杂配置,比如 pytest.ini 配置、conftest.py 配置等。
- 合理使用第三方插件,控制测试用例的执行顺序、分布式并发执行等场景。
- 掌握分层思想实现用例的分层,实现测试装置,测试数据,测试日志,测试报告等合理的框架构建。
- 开发一个插件,实现命令行传递参数
3.1.4 被测源码
class Calculator:
def add(self, a, b):
if a > 99 or a < -99 or b > 99 or b < -99:
print("请输入范围为【-99, 99】的整数或浮点数")
return "参数大小超出范围"
return a + b
def div(self, a, b):
if a > 99 or a < -99 or b > 99 or b < -99:
print("请输入范围为【-99, 99】的整数或浮点数")
return "参数大小超出范围"
return a / b
3.2 实现过程
3.2.1 代码提交
源码地址:Calculator_Project: Calculator
3.2.2 需求分析
3.2.3 测试计划(简版)
3.2.4 测试用例设计
3.2.5 Pytest自动化测试设计
- utils.py
from pandas.tests.io.excel.test_openpyxl import openpyxl
class Utils:
@classmethod
def get_excel_data(cls, excel_path, sheetname):
"""
读取 excel文件中指定 sheet 页的数据
:param excel_path: excel文件的路径
:param sheetname: sheet页的名称
:return: 返回读取的数据
"""
# 打开 Excel 文件
book = openpyxl.load_workbook(excel_path)
# 获取指定名称的工作表
sheet = book[sheetname]
# 初始化一个空列表,用于存储行数据
values = []
for row in sheet.iter_rows(values_only=True):
values.append(row)
return values
- test_add_excel.py
import sys
import pytest
import os
import allure
from Calculator_Project.base.base import Base
from Calculator_Project.utils.log_util import logger
from Calculator_Project.utils.utils import Utils
# 定义一个获取加法数据的函数,接收一个参数 level
@pytest.mark.parametrize("type",
["有效等价类", "无效等价类", "边界值", "错误推测"]
)
def get_add_data(type):
"""
读取加法的测试数据
:param type: 用例类型
:return: 对应优先级的测试数据和 ids
"""
# 获取当前文件的系统路径
root_path = os.path.dirname(os.path.abspath(__file__))
# 打印当前文件的系统路径
print(f"当前系统路径:{root_path}")
# 拼接 Excel文件的路径
excel_path = os.sep.join([root_path, '..', 'datas', 'calculator.xlsx'])
# 打印 Excel文件的路径
print(f"excel文件路径:{excel_path}")
sheetname = "加法"
# 获取 Excel 数据
excel_data = Utils.get_excel_data(excel_path, sheetname)
# 打印 Excel 数据
print(excel_data)
# 初始化空列表,用于存储对应的测试数据
datas = []
ids = []
# 遍历数据行,跳过标题行
for row in excel_data[1:]:
# 获取对应 level 优先级的数据
if row[0] == type:
datas.append([row[1], row[2], row[3]]) # 返回 a, b, expect
ids.append(row[4]) # 返回 ids
# 返回测试数据和测试标题的数组
return [datas, ids]
# 设置allure报告的模块名称
@allure.epic("计算器测试")
# 设置allure报告的模块名称
@allure.feature("计算器加法测试")
# 定义一个加法测试类,继承自Base类
class TestAddExcel(Base):
# 设置测试用例的执行顺序
@pytest.mark.run(order=1)
# 设置allure报告的用户故事
@allure.story("加法-有效等价类的用例")
# 使用 pytest的参数化装饰器,将get_add_datas("有效等价类")的结果解包成多个参数值,生成测试用例
@pytest.mark.parametrize(
"a, b, expect", get_add_data("有效等价类")[0], ids=get_add_data("有效等价类")[1]
)
# 定义一个测试加法的方法,接收3个参数:a, b, expect
def test_add_valid(self, a, b, expect):
# 添加图片
allure.attach.file("./datas/pic2.jpg",
name="计算器图片2",
attachment_type=allure.attachment_type.JPG,
extension="JGP")
# 记录测试用例参数的日志信息
logger.info(f"a={a}, b={b}, expect={expect}")
# 设置allure报告的测试步骤
with allure.step(f"1. 调用被测程序进行计算{a}+{b}={expect}"):
# 测试步骤:调用被测应用进行计算
result = self.calculator.add(a, b)
# 记录计算结果的日志信息
logger.info(f"实际计算结果为:{result}")
# 设置allure报告的测试步骤
with allure.step(f"2. 断言{result} == {expect}"):
# 断言实际结果是否与预期结果一致
assert result == expect
# 设置测试用例的执行顺序
@pytest.mark.run(order=4)
# 设置allure报告的用户故事
@allure.story("加法-无效等价类的用例")
# 使用 pytest的参数化装饰器,将get_add_datas("无效等价类")的结果解包成多个参数值,生成测试用例
@pytest.mark.parametrize(
"a,b,expect", get_add_data("无效等价类")[0], ids=get_add_data("无效等价类")[1]
)
# 定义一个测试加法的方法,接收3个参数:a, b, expect
def test_add_unvalid(self, a, b, expect):
# 记录测试用例参数的日志信息
logger.info(f"a={a}, b={b}, expect={expect}")
# 设置allure报告的测试步骤
with allure.step(f"1. 调用被测程序进行计算{a}+{b}={expect}"):
# 测试步骤:调用被测应用进行计算
result = self.calculator.add(a, b)
# 记录计算结果的日志信息
logger.info(f"实际计算结果为:{result}")
# 设置allure报告的测试步骤
with allure.step(f"2. 断言{result} == {expect}"):
# 断言实际结果是否与预期结果一致
assert result == expect
# 设置测试用例的执行顺序
@pytest.mark.run(order=2)
# 设置allure报告的用户故事
@allure.story("加法-边界值的用例")
# 使用 pytest的参数化装饰器,将get_add_datas("边界值")的结果解包成多个参数值,生成测试用例
@pytest.mark.parametrize(
"a, b, expect", get_add_data("边界值")[0], ids=get_add_data("边界值")[1]
)
# 定义一个测试加法的方法,接收3个参数:a, b, expect
def test_add_boundary(self, a, b, expect):
# 记录测试用例参数的日志信息
logger.info(f"a={a}, b={b}, expect={expect}")
# 设置allure报告的测试步骤
with allure.step(f"1. 调用被测程序进行计算{a}+{b}={expect}"):
# 测试步骤:调用被测应用进行计算
result = self.calculator.add(a, b)
# 记录计算结果的日志信息
logger.info(f"实际计算结果为:{result}")
# 设置allure报告的测试步骤
with allure.step(f"2. 断言{result} == {expect}"):
# 断言实际结果是否与预期结果一致
assert result == expect
# 设置测试用例的执行顺序
@pytest.mark.run(order=3)
# 设置allure报告的用户故事
@allure.story("加法-错误推测的用例")
# 使用 pytest的参数化装饰器,将get_add_datas("错误推测")的结果解包成多个参数值,生成测试用例
@pytest.mark.parametrize(
"a,b,expect", get_add_data("错误推测")[0], ids=get_add_data("错误推测")[1]
)
# 定义一个测试加法的方法,接收3个参数:a, b, expect
def test_add_type_error(self, a, b, expect):
# 记录测试用例参数的日志信息
logger.info(f"a={a}, b={b}, expect={expect}")
# 捕获预期的异常
with pytest.raises(eval(expect)) as e:
# 设置allure报告的测试步骤
with allure.step(f"1. 调用被测程序进行计算{a}+{b}={expect}"):
# 测试步骤:调用被测应用进行计算
result = self.calculator.add(a, b)
# 记录计算结果的日志信息
logger.info(f"实际计算结果为:{result}")
# 记录捕获的异常信息
logger.info(f"类型错误为:{e}")
# 设置allure报告的测试步骤
with allure.step(f"2. 断言{e.type} == {TypeError}"):
assert e.type == TypeError
# 设置测试用例的执行顺序
@pytest.mark.run(order=5)
# 设置始终跳过的测试用例
@pytest.mark.skip
def test_error_message(self):
print("详细提示信息未完成开发,暂不测试。")
assert True
# 设置测试用例的执行顺序
@pytest.mark.run(order=7)
# 设置特定条件下跳过的测试用例
@pytest.mark.skipif(sys.platform == "darwin", reson = "不做mac的兼容")
def test_sys(self):
print("Mac系统不做兼容测试")
assert True
# 设置测试用例的执行顺序
@pytest.mark.run(order=6)
# 预期失败的测试用例
@pytest.mark.xfail
def test_fail(self):
print("标记期望失败的案例,还没想好哪些失败。。。")
assert True
- test_div_excel.py
import sys
import pytest
import os
import allure
from Calculator_Project.base.base import Base
from Calculator_Project.utils.log_util import logger
from Calculator_Project.utils.utils import Utils
# 定义一个获取除法数据的函数,接收一个参数 level
@pytest.mark.parametrize("type",
["有效等价类", "无效等价类", "边界值", "错误推测", "被除数为0"]
)
def get_div_data(type):
"""
读取除法的测试数据
:param type: 用例类型
:return: 对应优先级的测试数据和 ids
"""
# 获取当前文件的系统路径
root_path = os.path.dirname(os.path.abspath(__file__))
# 打印当前文件的系统路径
print(f"当前系统路径:{root_path}")
# 拼接 Excel文件的路径
excel_path = os.sep.join([root_path, '..', 'datas', 'calculator.xlsx'])
# 打印 Excel文件的路径
print(f"excel文件路径:{excel_path}")
# 获取 Excel 数据
excel_data = Utils.get_excel_data(excel_path, "除法")
# 打印 Excel 数据
print(excel_data)
# 初始化空列表,用于存储对应的测试数据
datas = []
ids = []
# 遍历数据行,跳过标题行
for row in excel_data[1:]:
# 获取对应 level 优先级的数据
if row[0] == type:
datas.append([row[1], row[2], row[3]]) # 返回 a, b, expect
ids.append(row[4]) # 返回 ids
# 返回测试数据和测试标题的数组
return [datas, ids]
# 设置allure报告的模块名称
@allure.epic("计算器测试")
# 设置allure报告的模块名称
@allure.feature("计算器除法测试")
# 定义一个除法测试类,继承自Base类
class TestDivExcel(Base):
# 设置测试用例的执行顺序
@pytest.mark.run(order=1)
# 设置allure报告的用户故事
@allure.story("除法-有效等价类的用例")
# 使用 pytest的参数化装饰器,将get_div_datas("有效等价类")的结果解包成多个参数值,生成测试用例
@pytest.mark.parametrize(
"a, b, expect", get_div_data("有效等价类")[0], ids=get_div_data("有效等价类")[1]
)
# 定义一个测试除法的方法,接收3个参数:a, b, expect
def test_div_valid(self, a, b, expect):
# 添加图片
allure.attach.file("./datas/pic1.jpg",
name="计算器图片1",
attachment_type=allure.attachment_type.JPG,
extension="JGP")
# 记录测试用例参数的日志信息
logger.info(f"a={a}, b={b}, expect={expect}")
# 设置allure报告的测试步骤
with allure.step(f"1. 调用被测程序进行计算{a}/{b}={expect}"):
# 测试步骤:调用被测应用进行计算
result = self.calculator.div(a, b)
# 记录计算结果的日志信息
logger.info(f"实际计算结果为:{result}")
# 设置allure报告的测试步骤
with allure.step(f"2. 断言{result} == {expect}"):
# 断言实际结果是否与预期结果一致
assert result == expect
# 设置测试用例的执行顺序
@pytest.mark.run(order=4)
# 设置allure报告的用户故事
@allure.story("除法-无效等价类的用例")
# 使用 pytest的参数化装饰器,将get_div_datas("无效等价类")的结果解包成多个参数值,生成测试用例
@pytest.mark.parametrize(
"a,b,expect", get_div_data("无效等价类")[0], ids=get_div_data("无效等价类")[1]
)
# 定义一个测试除法的方法,接收3个参数:a, b, expect
def test_div_unvalid(self, a, b, expect):
# 记录测试用例参数的日志信息
logger.info(f"a={a}, b={b}, expect={expect}")
# 设置allure报告的测试步骤
with allure.step(f"1. 调用被测程序进行计算{a}/{b}={expect}"):
# 测试步骤:调用被测应用进行计算
result = self.calculator.div(a, b)
# 记录计算结果的日志信息
logger.info(f"实际计算结果为:{result}")
# 设置allure报告的测试步骤
with allure.step(f"2. 断言{result} == {expect}"):
# 断言实际结果是否与预期结果一致
assert result == expect
# 设置测试用例的执行顺序
@pytest.mark.run(order=2)
# 设置allure报告的用户故事
@allure.story("除法-边界值的用例")
# 使用 pytest的参数化装饰器,将get_div_datas("边界值")的结果解包成多个参数值,生成测试用例
@pytest.mark.parametrize(
"a, b, expect", get_div_data("边界值")[0], ids=get_div_data("边界值")[1]
)
# 定义一个测试除法的方法,接收3个参数:a, b, expect
def test_div_boundary(self, a, b, expect):
# 记录测试用例参数的日志信息
logger.info(f"a={a}, b={b}, expect={expect}")
# 设置allure报告的测试步骤
with allure.step(f"1. 调用被测程序进行计算{a}/{b}={expect}"):
# 测试步骤:调用被测应用进行计算
result = self.calculator.div(a, b)
# 记录计算结果的日志信息
logger.info(f"实际计算结果为:{result}")
# 设置allure报告的测试步骤
with allure.step(f"2. 断言{result} == {expect}"):
# 断言实际结果是否与预期结果一致
assert result == expect
# 设置测试用例的执行顺序
@pytest.mark.run(order=3)
# 设置allure报告的用户故事
@allure.story("除法-错误推测的用例")
# 使用 pytest的参数化装饰器,将get_div_datas("错误推测")的结果解包成多个参数值,生成测试用例
@pytest.mark.parametrize(
"a,b,expect", get_div_data("错误推测")[0], ids=get_div_data("错误推测")[1]
)
# 定义一个测试除法的方法,接收3个参数:a, b, expect
def test_div_type_error(self, a, b, expect):
# 记录测试用例参数的日志信息
logger.info(f"a={a}, b={b}, expect={expect}")
# 捕获预期的异常
with pytest.raises(eval(expect)) as e:
# 设置allure报告的测试步骤
with allure.step(f"1. 调用被测程序进行计算{a}/{b}={expect}"):
# 测试步骤:调用被测应用进行计算
result = self.calculator.div(a, b)
# 记录计算结果的日志信息
logger.info(f"实际计算结果为:{result}")
# 记录捕获的异常信息
logger.info(f"类型错误为:{e}")
# 设置allure报告的测试步骤
with allure.step(f"2. 断言{e.type} == {TypeError}"):
assert e.type == TypeError
# 设置测试用例的执行顺序
@pytest.mark.run(order=5)
# 设置allure报告的用户故事
@allure.story("除法-被除数为0的用例")
# 使用 pytest的参数化装饰器,将get_div_datas("被除数为0")的结果解包成多个参数值,生成测试用例
@pytest.mark.parametrize(
"a, b, expect", get_div_data("被除数为0")[0], ids=get_div_data("被除数为0")[1]
)
# 定义一个测试除法的方法,接收3个参数:a, b, expect
def test_div_zero(self, a, b, expect):
# 记录测试用例参数的日志信息
logger.info(f"a={a}, b={b}, expect={expect}")
# 捕获预期的异常
with pytest.raises(eval(expect)) as e:
# 设置allure报告的测试步骤
with allure.step(f"1. 调用被测程序进行计算{a}/{b}={expect}"):
# 测试步骤:调用被测应用进行计算
result = self.calculator.div(a, b)
# 记录计算结果的日志信息
logger.info(f"实际计算结果为:{result}")
# 记录捕获的异常信息
logger.info(f"类型错误为:{e}")
# 设置allure报告的测试步骤
with allure.step(f"2. 断言{e.type} == ZeroDivisionError"):
assert e.type == ZeroDivisionError
# 设置测试用例的执行顺序
@pytest.mark.run(order=8)
# 设置始终跳过的测试用例
@pytest.mark.skip
def test_error_message(self):
print("详细提示信息未完成开发,暂不测试。")
assert True
# 设置测试用例的执行顺序
@pytest.mark.run(order=7)
# 设置特定条件下跳过的测试用例
@pytest.mark.skipif(sys.platform == "darwin", reson = "不做mac的兼容")
def test_sys(self):
print("Mac系统不做兼容测试")
assert True
# 设置测试用例的执行顺序
@pytest.mark.run(order=6)
# 预期失败的测试用例
@pytest.mark.xfail
def test_fail(self):
print("标记期望失败的案例,还没想好哪些失败。。。")
assert True
- conftest.py
import pytest
import yaml
# 解决用例描述中的中文乱码问题
# 定义一个pytest的钩子函数,接收3个参数 session, config, items,返回值类型都为 None
# 主要用于在收集测试用例后,对其进行修改
def pytest_collection_modifyitems(session, config, items):
# 循环遍历所有收集到的测试用例的 items
for item in items:
# 对每个 item的 name进行编码和解码
# item.name.encode('utf-8')是将 item按照 UTF-8格式编码
# decode("unicode-escape")是将编码后的字符串按照 unicode-escape格式解码,以便正确显示中文字符。
item.name = item.name.encode("utf-8").decode("unicode-escape")
# 对每个 item的 _nodeid进行编码和解码
item._nodeid = item._nodeid.encode("utf-8").decode("unicode-escape")
- pytest.ini
[pytest]
# 日志开关 true false
log_cli = true
# 日志级别
log_cli_level = info
# 使用4个并发进程运行测试
# 打印详细日志,相当于命令行加 -vs
# 在第3个失败的测试后停止执行
addopts = -n 4 --capture=no --maxfail=3
# 日志格式
log_cli_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
# 日志时间格式
log_cli_date_format = %Y-%m-%d %H:%M:%S
# 日志文件位置
log_file = ./log/test.log
# 日志文件等级
log_file_level = info
# 日志文件格式
log_file_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
# 日志文件日期格式
log_file_date_format = %Y-%m-%d %H:%M:%
# 设置执行的路径
testpaths = tests
# 匹配测试文件的命名模式
python_files = test_*.py
# 匹配测试类的命名模式
python_classes = Test*
# 匹配测试函数的命名模式
python_functions = test_*
markers =
run: specify order of test exection
- log_util.py
import logging
import os
from logging.handlers import RotatingFileHandler
# 绑定绑定句柄到logger对象
logger = logging.getLogger(__name__)
# 获取当前工具文件所在的路径
root_path = os.path.dirname(os.path.abspath(__file__))
# 拼接当前要输出日志的路径
log_dir_path = os.sep.join([root_path, '..', f'/logs'])
if not os.path.isdir(log_dir_path):
os.mkdir(log_dir_path)
# 创建日志记录器,指明日志保存路径,每个日志的大小,保存日志的上限
file_log_handler = RotatingFileHandler(os.sep.join([log_dir_path, 'log.log']), maxBytes=1024 * 1024, backupCount=10)
# 设置日志的格式
date_string = '%Y-%m-%d %H:%M:%S'
formatter = logging.Formatter(
'[%(asctime)s] [%(levelname)s] [%(filename)s]/[line: %(lineno)d]/[%(funcName)s] %(message)s ', date_string)
# 日志输出到控制台的句柄
stream_handler = logging.StreamHandler()
# 将日志记录器指定日志的格式
file_log_handler.setFormatter(formatter)
stream_handler.setFormatter(formatter)
# 为全局的日志工具对象添加日志记录器
# 绑定绑定句柄到logger对象
logger.addHandler(stream_handler)
logger.addHandler(file_log_handler)
# 设置日志输出级别
logger.setLevel(level=logging.INFO)
3.2.6 Allure报告
- 在线Allure报告
- 静态Allure报告