1. 接口关键字封装
1.1 基本概念
接口关键字封装是指:将接口测试过程中常用的操作、验证封装成可复用的关键字(或称为函数、方法),以提高测试代码的可维护性和可复用性。
1.2 常见的接口关键字封装方式
1. 发送请求:封装一个函数,接受参数如请求方法、URL、请求头、请求体等,使用相应的库发送请求,如requests库或HttpClient库。---------------->>>>2. 响应验证:封装一个函数,接受参数如响应对象、预期结果等,通过断言或其他方式验证响应的状态码、响应体等是否符合预期。---------------->>>>3. 数据提取:封装一个函数,接受参数如响应体、提取表达式等,使用正则表达式XPath、JSONPath等方式提取需要的数据,并返回提取结果。---------------->>>>4. 数据保存:封装一个函数,接受参数如文件路径、数据等,将数据保存到指定的文件中,如Excel、CSV、数据库等。---------------->>>>5. 参数化配置:封装一个函数,接受参数如环境配置、数据文件路径等,根据不同的环境或数据文件读取对应的配置信息,如接口URL、认证信息等。---------------->>>>封装的目的:通过封装接口关键字,可以使接口测试代码更简洁、可读性更高,并且提供了灵活性和扩展性,方便维护和管理测试代码。
2. 接口封装实战应用
2.1 接口关键字封装代码示例
这是接口关键字驱动类,用于提供自动化接口测试的关键字方法。主要是实现常用的关键字内容,并定义好所有的参数内容即可接口中常用关键字:1.各种模拟请求方法:Post/get/put/delete/header/....2.集合Allure,可添加@allure.step,这样在自动化执行的时候,Allure报告可以直接捕捉相关的执行信息,让测试报告更详细3.根据需求进行断言封装:jsonpath、数据库断言。----------------------->>>封装get、post请求示例:
import requests
import jsonpath
import json
"""
关键字驱动/基类/工具类:
1. 发送请求:8种:post、get、put、delete...
2. 提取数据
补充知识点:方法的缺省值:params=None (默认值),如果没有传参则默认为None
"""
class ApiKey:
def get(self, url, params=None, **kwargs):
"""
发送get请求
:param url:接口请求url
:param params: 拼接在url的参数
:param kwargs: 其它的参数
:return: 返回请求后的数据
"""
print(">>>>>>开始发送Get请求")
return requests.get(url=url, params=params, **kwargs)
def post(self, url, data=None, json=None, **kwargs):
"""
发送post请求
:param url: 接口请求url
:param data: data的请求数据
:param json: json的请求数据
:param kwargs: 其它的参数
:return: 返回请求后的数据
"""
print(">>>>>>开始发送Post请求")
res = requests.post(url=url, data=data, json=json, **kwargs)
print(">>>>>>响应数据为:", res.json())
return res
2.2 封装实现完整的登录接口
基于上面的封装代码:
实现一个项目的登录接口(post请求)、并添加josnpath提取响应信息
import requests
import jsonpath
import json
class ApiKey:
def post(self, url, data=None, json=None, **kwargs):
"""
发送post请求
:param url: 接口请求url
:param data: data的请求数据
:param json: json的请求数据
:param kwargs: 其它的参数
:return: 返回请求后的数据
"""
print(">>>>>>开始发送Post请求")
res = requests.post(url=url, data=data, json=json, **kwargs)
print(">>>>>>响应数据为:", res.json())
return res
def get_text(self, response, key):
"""
提取json当中的某个值
:param response: 需要提取的json数据,比如:{"msg":"登录成功"}
:param key: 对应要提取的jsonpath,比如: $.msg
:return: 返回提取数据之后的【第一个值】
"""
print(">>>>>>开始提取JsonPath响应数据")
value_list = jsonpath.jsonpath(response, key)
print(">>>>>>提取数据响应为:", value_list[0])
return value_list[0]
# 主函数:
if __name__ == '__main__':
# 1. 实例化对象:ApiKey
ak = ApiKey()
# 2. 通过对应的类调用对应的方法 --四要素
url = "http://xxx.com/index.php?s=/api/user/login"
# 公共参数
pulic_data = {"application": "app", "application_client_type": "weixin"}
# 请求参数-- body (你的body数据是要以json进行提交,参数:json)
data = {"accounts": "hailey", "pwd": "hailey123", "type": "username"}
res = ak.post(url=url, params=pulic_data, data=data)
# 3. 提取数据
text = ak.get_text(res.json(), "$.msg")
为了代码的健壮性,添加提取数据的格式判断:
如果返回的响应数据格式是json字符串"{"msg":"登录成功"}",则进行类型转换:
import requests
import jsonpath
import json
class ApiKey:
def post(self, url, data=None, json=None, **kwargs):
"""
发送post请求
:param url: 接口请求url
:param data: data的请求数据
:param json: json的请求数据
:param kwargs: 其它的参数
:return: 返回请求后的数据
"""
print(">>>>>>开始发送Post请求")
res = requests.post(url=url, data=data, json=json, **kwargs)
print(">>>>>>响应数据为:", res.json())
return res
def get_text(self, response, key):
"""
提取json当中的某个值
:param response: 需要提取的json数据,比如:{"msg":"登录成功"}
:param key: 对应要提取的jsonpath,比如: $.msg
:return: 返回提取数据之后的【第一个值】
"""
# 为了代码的健壮性,添加判断是否为字符串(防止响应信息格式为json字符串)
if isinstance(response,str):
#是字符串,我就让它转一下类型
response = json.loads(response)
print(">>>>>>开始提取JsonPath响应数据")
value_list = jsonpath.jsonpath(response, key)
print(">>>>>>提取数据响应为:", value_list[0])
return value_list[0]
# 主函数:
if __name__ == '__main__':
# 1. 实例化对象:ApiKey
ak = ApiKey()
res ='{"msg":"登录成功"}'
text = ak.get_text(res, "$.msg")
2.3 测试用例封装
这里,我们使用pytest框架进行测试用例的编写与封装
在之前Pytest的学习中,我们了解到:
需要将测试用例新建一个项目文件,一个是用例主体,放测试用例,另一主体是用例运行main文件;之前pytest使用详情戳:pytest -- 基本使用详解_pytest 具体能做哪些事情-CSDN博客、pytest -- 进阶使用详解_怎么通过pytest-html插件查看pytest结果-CSDN博客
现在我们在项目文件下新增一个已经封装好的登录功能文件(即上面的示例代码)
文件结构
测试用例
在testcase文件下,首先新建一个'test_ds_01.py'文件(登陆的测试用例):
# 导入:从项目根目录的下一级开始写
from Requests_demo.day06.PytestFrame.api_keyword.api_key import ApiKey
# 方法:要以test 【默认的规则】
def test_loging():
# 1. 实例化对象:ApiKey
ak = ApiKey()
# 2. 通过对应的类调用对应的方法 --四要素
url = "http://xxxx.com/index.php?s=/api/user/login"
pulic_data = {"application": "app", "application_client_type": "weixin"}
data = {"accounts": "hailey", "pwd": "hailey123", "type": "username"}
# 3. 发送请求
res = ak.post(url=url, params=pulic_data, data=data)
# 4. 提取数据
text = ak.get_text(res.json(), "$.msg")
# 5. 进行断言
assert text == "登录成功", "期望结果和实际结果不一致"
"""
扩展(不建议):
如果你在当前的文件去进行调试的话,需要把运行模式调整成:unittest
为什么要调整:因为我们pytest的一些运行规则unittest不支持
但是,我们用的是pytest ,它支持的是通过文件的入口去调试
"""
# test_loging()
用例调试_ 扩展知识(错误示范)
不建议直接在测试用例模块中直接运行调试(直接调试就是把上面代码'test_loging()'这行放开):
运行后控制台会显示左右两块面板,它是uinttest的运行模式
因为我们用的是pytest ,它支持的是通过文件的入口去调试(项目文件下的main_run文件)
仅作为扩展,不建议:
如果你在当前的文件去进行调试的话,需要把运行模式调整成:unittest
为什么要调整?因为我们pytest的一些运行规则unittest不支持
unittest模块切换方式如下:
设置完,再来运行,就变成一块面板了:
正确姿势:通过main文件运行用例
# file name:main.run.py
# 框架的运行入口
import pytest
if __name__ == '__main__':
"""
默认找当前项目文件下test开头、test结尾的模块运行; ./表示当前目录
也可以指定文件路径运行(当前项目下其他用例模块),例如:(["-vs","./testcase/test_ds_022.py"])
"""
pytest.main(["-vs"])
2.4 Pytest + Allure生成测试报告
这里我们使用Pytest + Allure生成测试报告,首先需要安装pytest、alluer的相关包
具体可以看我之前的文章:
pytest测试框架-- 基本功能使用详解_pytest 具体能做哪些事情-CSDN博客
pytest进阶 -- 结合Html / Allure生成测试报告_怎么通过pytest-html插件查看pytest结果-CSDN博客
代码示例
只要按照搞定了安装,基于上面的代码,只需要在main文件中加两三行代码就能实现:
# 框架的运行入口
import pytest
import os
if __name__ == '__main__':
"""
默认找当前项目文件下test开头、test结尾的模块运行; ./(当前目录)
也可以指定文件路径运行,例如:(["-vs","./testcase/test_ds_022.py"])
"""
# pytest.main(["-vs"])
"""
通过pytest运行,并生成alluer报告:
第一步:运行指定文件,运行结果的数据放在./result;--clena-alluerdir(每次运行之前自动清空历史数据)
"""
pytest.main(["-vs", "./testcase","--alluredir", "./result", "--clean-alluredir"])
# 第二步:将运行结果生成测试报告,固定写法如下:
os.system("allure generate ./result -o ./report_allure --clean")
第二步的代码,也可以直接在pycharm的Terminal终端直接输入命令:
pytest -vs --alluredir=./result --clean-alluredir ./testcase
我还是觉得直接写在main文件中更加方面,不然每次都得手动敲一遍~...
运行后,在当前项目文件下就多了result、report_allure两个文件
测试报告:
2.5 Allure报告优化
测试步骤优化
通过添加对应的测试步骤:@allure.step("步骤内容注释")@allure.title("测试用例标题")
① 封装的登录模块添加步骤注释:
即:api_keyword/api_key.py模块
每个方法的上面都加上@allure.step:
# file name:api_key.py
import allure
import requests
import jsonpath
import json
"""
关键字驱动/基类/工具类:
1. 发送请求:8种:post、get、put、delete...
2. 提取数据
补充知识点:方法的缺省值:params=None (默认值),如果没有传参则默认为None
"""
class ApiKey:
@allure.step(">>>>>>开始发送Get请求")
def get(self, url, params=None, **kwargs):
"""
发送get请求
:param url:接口请求url
:param params: 拼接在url的参数
:param kwargs: 其它的参数
:return: 返回请求后的数据
"""
return requests.get(url=url, params=params, **kwargs)
@allure.step(">>>>>>开始发送Post请求")
def post(self, url, data=None, json=None, **kwargs):
"""
发送post请求
:param url: 接口请求url
:param data: data的请求数据
:param json: json的请求数据
:param kwargs: 其它的参数
:return: 返回请求后的数据
"""
res = requests.post(url=url, data=data, json=json, **kwargs)
print(">>>>>>响应数据是:", res.json())
return res
@allure.step(">>>>>>开始提取响应数据")
def get_text(self, response, key):
"""
提取json当中的某个值
:param response: 需要提取的json数据,比如:{"msg":"登录成功"}
:param key: 对应要提取的jsonpath,比如: $.msg
:return: 返回提取数据之后的【第一个值】
"""
if isinstance(response,str):
# 如果响应数据是字符串,我就让它转一下类型
response = json.loads(response)
value_list = jsonpath.jsonpath(response, key)
print(">>>>>>提取的响应数据是:", value_list[0])
return value_list[0]
② 测试用例:test_ds_01.py
添加@allure.title("测试用例标题")
import allure
# 导入:从项目根目录的下一级开始写
from Requests_demo.day06.PytestFrame.api_keyword.api_key import ApiKey
# 方法:要以test 【默认的规则】
@allure.title("登陆接口_测试用例")
def test_loging():
# 1. 实例化对象:ApiKey
ak = ApiKey()
# 2. 通过对应的类调用对应的方法 --四要素
url = "http://xxx.com/index.php?s=/api/user/login"
pulic_data = {"application": "app", "application_client_type": "weixin"}
data = {"accounts": "hailey", "pwd": "hailey123", "type": "username"}
# 3. 发送请求
res = ak.post(url=url, params=pulic_data, data=data)
# 4. 提取数据
text = ak.get_text(res.json(), "$.msg")
# 5. 进行断言
assert text == "登录成功", "期望结果和实际结果不一致"
重新执行main文件,打开测试报告,就会显示测试用例的名称、步骤:
增加了测试报告的可阅读性
接口关联优化
接口关联,指的是上下游依赖的接口,放在同一个测试用例中进行执行
这里测试项目背景是某电商平台:
所以第一个接口是上面提到的登录,通过登录后拿到token进行第二个接口:加入购物车
file name:test_ds_02.py
import allure
# 导入:从项目根目录的下一级开始写
from Requests_demo.day06.PytestFrame.api_keyword.api_key import ApiKey
@allure.title("登录测试成功并且加入购物车")
def test_addcard():
with allure.step("第一步:登录接口的调用"):
# 1. 实例化对象:ApiKey
ak = ApiKey()
# 2. 通过对应的类调用对应的方法 --四要素
url = "http://xxx.com/index.php?s=/api/user/login"
pulic_data = {"application": "app", "application_client_type": "weixin"}
data = {"accounts": "hailey", "pwd": "hailey123", "type": "username"}
# 3. 发送请求
res = ak.post(url=url, params=pulic_data, data=data)
# 4. 提取数据
text = ak.get_text(res.json(), "$.msg")
token = ak.get_text(res.json(), "$..token")
# 5. 进行断言
assert text == "登录成功", "期望结果和实际结果不一致"
with allure.step("第二步:加入购物车接口调用"):
# 接口二: 加入购物车
url = "http://xxx.com/index.php?s=/api/cart/save"
public_data = {"application": "app", "application_client_type": "weixin", "token": token}
data = {
"goods_id": "11",
"spec": [
{
"type": "尺寸",
"value": "M"
}
],
"stock": "10"
}
res = ak.post(url=url, params=public_data, data=data)
# 获取响应数据
result = res.json()
print(f"响应结果是:{result}")
# 期望结果
desire_res = "加入成功"
# 实际结果
reality_res = ak.get_text(result, "$.msg")
assert desire_res == reality_res, "期望结果和实际结果不一致"
因为新建了一个用例模块,所以main文件,稍稍改动,指定‘test_ds_02.py’文件运行
import pytest
import os
if __name__ == '__main__':
pytest.main(["-vs", "./testcase/test_ds_02.py","--alluredir", "./result", "--clean-alluredir"])
os.system("allure generate ./result -o ./report_allure --clean")
执行后,打开测试报告:
2.6 接口关联封装改进
设置全局变量
可以通过设置全局变量去实现, 仅在当前文件下有效(不推荐)先声明,后赋值语法:global 变量名
给第一个方法添加全局变量:
# file name:test_ds_03.py
import allure
# 导入:从项目根目录的下一级开始写
from Requests_demo.day06.PytestFrame.api_keyword.api_key import ApiKey
@allure.title("登录测试成功")
def test_login():
global ak, token # 设置全局变量
with allure.step("第一步:登录接口的调用"):
ak = ApiKey()
url = "http://xxx.com/index.php?s=/api/user/login"
pulic_data = {"application": "app", "application_client_type": "weixin"}
data = {"accounts": "hailey", "pwd": "hailey123", "type": "username"}
res = ak.post(url=url, params=pulic_data, data=data)
text = ak.get_text(res.json(), "$.msg")
token = ak.get_text(res.json(), "$..token")
assert text == "登录成功", "期望结果和实际结果不一致"
@allure.title("加入购物车")
def test_addcard():
with allure.step("第二步:加入购物车接口调用"):
# 接口二: 加入购物车
url = "http://xxx.com/index.php?s=/api/cart/save"
public_data = {"application": "app", "application_client_type": "weixin", "token": token}
data = {
"goods_id": "11",
"spec": [
{
"type": "尺寸",
"value": "M"
}
],
"stock": "10"
}
res = ak.post(url=url, params=public_data, data=data)
result = res.json()
print(f"响应结果是:{result}")
desire_res = "加入成功"
reality_res = ak.get_text(result, "$.msg")
assert desire_res == reality_res, "期望结果和实际结果不一致"
重新运行main文件,查看测试报告:
项目级token多文件关联
fixture+conftest实现项目级的token多文件关联:
在软件测试中,Fixture 是一种用于管理测试环境和测试数据的机制。它允许在测试函数或方法运行之前和之后执行特定的代码,以确保测试的可重复性和一致性。Fixture主要用于设置测试环境、准备测试数据、执行清理操作等,以便能够按预期运行。=====================语法结构:@pytest.fixture() 装饰器在 pytest 测试框架中用于定义测试夹具(fixture),并可以指定夹具的作用域。pytest 提供了四种作用域选项:1. function (默认):作用范围限于单个测试函数。每个测试函数执行前都会调用该夹具。2. class :作用范围限于测试类中的所有测试方法。每个测试类执行前都会调用该夹具。3. module :作用范围限于单个测试模块。每个测试模块执行前都会调用该夹具。4. session :作用范围限于整个测试会话。在整个测试会话期间只会调用一次该夹具。------------>>简单来说:就是某个变量,在整个项目的全局都需要用到,就可以利用 pytest的测试夹具去进行实现---固定的命令conftest.py
def my_fixture():
# 夹具的具体实现
...
代码示例:
测试夹具:conftest,相当于把登陆接口、token作为整个项目的全局变量
# file name:conftest.py(文件名固定,不能修改)
import pytest
from Requests_demo.day06.PytestFrame.api_keyword.api_key import ApiKey
"""
可以写具体的方法、正常的调用某个接口
pytest在运行的时候【会自动】先调用这个文件,后面还具体调用这个方法
方法:获取token
1. 正常的请求对应的接口并且提取数据
2. 告诉别人这个是一个测试夹具(测试前置、后置操作):@pytest.fixture()
"""
@pytest.fixture(scope= "session")
def token_fix():
ak = ApiKey()
url = "http://xxx.com/index.php?s=/api/user/login"
pulic_data = {"application": "app", "application_client_type": "weixin"}
data = {"accounts": "hailey", "pwd": "hailey123", "type": "username"}
res = ak.post(url=url, params=pulic_data, data=data)
token = ak.get_text(res.json(), "$..token")
return token,ak
测试用例: 注意token,ak的前后顺序,要与conftest中的顺序一致,如不一致则会报错
# file name:test_ds-04.py
import allure
@allure.title("加入购物车")
def test_addcard(token_fix):
token , ak = token_fix
with allure.step("第二步:加入购物车接口调用"):
# 接口二: 加入购物车
url = "http://xxxx.com/index.php?s=/api/cart/save"
public_data = {"application": "app", "application_client_type": "weixin", "token": token}
data = {
"goods_id": "11",
"spec": [
{
"type": "尺寸",
"value": "M"
}
],
"stock": "10"
}
res = ak.post(url=url, params=public_data, data=data)
# ----------------------------------------
# 获取响应数据
result = res.json()
print(f"响应结果是:{result}")
# 期望结果
desire_res = "加入成功31321312321"
# 实际结果
reality_res = ak.get_text(result, "$.msg")
assert desire_res == reality_res, "期望结果和实际结果不一致"
main文件:
import pytest
import os
if __name__ == '__main__':
pytest.main(["-v", "./testcase/test_ds_04.py","--alluredir", "./result", "--clean-alluredir"])
os.system("allure generate ./result -o ./report_allure --clean")
重新运行main文件,查看测试报告:
全局常量定义
比较固定的变量,不会随便修改的,称之为常量。
比如:常用的服务IP、端口(环境变量)、公共参数等,数据链接信息、测试账号/密码等,统一放到一个专门的文件,定义为常量,方便维护修改。
===================
操作步骤 :1. 新建一个py文件,专门用来存放常量,一般常量的命名都是大写;2. 对应的文件导入这个模块,即可使用。如下,新建一个config.py文件放在项目根目录下:
这里我把环境地址、测试账号/密码、请求的公共参数,全部进行参数化设置:
# 配置常量的文件;file name:config.py
# 环境URL:
PROJECT_URL = "http://xxx.com/index.php"
# 测试账号
USERNAME = "hailey"
PASSWORD = "hailey123"
LOGINTYPE = "username"
# 请求公共参数:
PULIC_DATA = {"application": "app", "application_client_type": "weixin"}
然后对应,修改conftest.py的代码:引用config文件的参数名即可
# file name:conftest.py
import pytest
from Requests_demo.day06.PytestFrame.api_keyword.api_key import ApiKey
from Requests_demo.day06.PytestFrame.config import * # 导入config文件所有内容
@pytest.fixture(scope= "session")
def token_fix():
ak = ApiKey()
url = PROJECT_URL+"?s=/api/user/login"
pulic_data = PULIC_DATA
data = {"accounts": USERNAME, "pwd": PASSWORD, "type": LOGINTYPE}
res = ak.post(url=url, params=pulic_data, data=data)
token = ak.get_text(res.json(), "$..token")
return token, ak
重新运行,测试报告的结果和之前一样:
3. Logging入门详解
3.1 Logging基本概述
使用标准库提供的 logging API 最主要的好处是:Python所有模块都可能参与日志输出,包括你自己的日志消息和第三方模块的日志消息。===============================记录程序日志信息的目的:● 可以很方便的了解程序的运行情况● 可以分析用户的操作行为、喜好等信息● 方便开发人员检查bug(Allure就可以支持)● 方便和运维系统对接,进行告警检测(必须输出日志文件,Allure无法解决,得用Logging)===============================官网文档链接:Python日志官网: https://docs.python.org/zh-cn/3/library/logging.htmlPytest日志官网: https://docs.pytest.org/en/latest/how-to/logging.html
3.2 logging日志级别介绍
日志等级可以分为5个,从低到高分别是:1.DEBUG2.INFO3.WARNING4.ERROR5.CRITICAL===============================日志等级从低到高的顺序是: DEBUG < INFO < WARNING < ERROR < CRITICAL===============================日志等级说明:DEBUG:程序调试bug时使用INFO:程序正常运行时使用WARNING:程序未按预期运行时使用,但并不是错误,如:用户登录密码错误ERROR:程序出错误时使用,如:IO操作失败CRITICAL:特别严重的问题,导致程序不能再继续运行时使用,如:磁盘空间为空,一般很少使用===============================默认的是WARNING等级,当在WARNING或WARNING之上等级的才记录日志信息。
3.3 logging日志的使用
在 logging 包中记录日志的方式有两种:
1. 输出到控制台2. 保存到日志文件
代码示例
import logging
# 设置全局日志级别为DEBUG
logging.basicConfig(level=logging.DEBUG)
logging.debug("debug信息")
logging.info("info信息")
logging.warning("warning信息")
logging.error("error信息")
logging.critical("critical信息")
3.4 logging日志格式使用大全
标识
| 描述 |
%(asctime)s
|
字符串形式的当前时间。
默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
|
%(levelname)s
| 打印日志级别名称 |
%(filename)s
| 打印当前执行程序名 |
%(lineno)d
| 打印日志的当前行号 |
%(message)s
| 打印日志信息 |
%(levelno)s
| 打印日志级别的数值 |
%(funcName)s
| 打印日志的当前函数 |
%(module)s
| 调用日志输出函数的模块名 |
%(pathname)s
| 打印当前执行程序的路径,其实就是sys.argv[0] |
%(name)s
|
所使用的日志名称,默认是'root',因为默认使用的是 rootLogger
|
%(msecs)d
| 日志事件发生事件的毫秒部分 |
% (relativeCreated)d
| 输出日志信息时的,自Logger创建以 来的毫秒数 |
%(threadName)s
| 打印线程名称 |
%(thread)d
| 打印线程ID |
%(process)d
| 打印进程ID |
代码示例
"""
日志格式化:
%(asctime)s :字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫
秒
%(levelname)s : 打印日志级别名称
%(filename)s : 打印当前执行程序名
%(lineno)d : 打印日志的当前行号
%(message)s : 打印日志信息
"""
import logging
# 设置全局日志级别为DEBUG
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)s %(filename)s(%(lineno)d) : %(message)s ")
logging.debug("debug信息".format())
logging.info("info信息")
logging.warning("warning信息")
logging.error("error信息")
logging.critical("critical信息")
3.5 logging日志写入文件
可以通过:logging.basicConfig(level=logging.DEBUG, format="日志格 式",stream=file对象)
代码示例
import logging
file = open("log.log", mode="a", encoding="utf-8")
# 设置全局日志级别为DEBUG
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)s %(filename)s(%(lineno)d) : %(message)s ",stream=file)
logging.debug("debug信息")
logging.info("info信息")
logging.warning("warning信息")
logging.error("error信息")
logging.critical("critical信息")
运行后,log文件信息如下:
3.6 logging日志封装
几个重要的概念● Logger 记录器暴露了应用程序代码直接使用的接口。● Handler 处理器将日志记录(由记录器创建)发送到适当的目标。● Filter 过滤器提供了更细粒度的功能,用于确定要输出的日志记录。● Formatter 格式器指定最终输出中日志记录的样式。
代码示例
import logging
import os
from logging.handlers import RotatingFileHandler
class Logger:
def __init__(self, name, log_file, level=logging.DEBUG):
self.logger = logging.getLogger(name)
self.logger.setLevel(level)
# 创建日志格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# 确保日志文件的目录存在
log_dir = os.path.dirname(log_file)
if not os.path.exists(log_dir):
os.makedirs(log_dir)
# 创建文件处理器
file_handler = RotatingFileHandler(log_file, maxBytes=1048576, backupCount=3)
file_handler.setFormatter(formatter)
self.logger.addHandler(file_handler)
# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
self.logger.addHandler(console_handler)
if __name__ == '__main__':
# 创建日志实例
logger = Logger('my_logger', '.\\logdata\\log.log')
# 输出日志
logger.logger.debug('This is a debug message')
logger.logger.info('This is an info message')
logger.logger.warning('This is a warning message')
logger.logger.error('This is an error message')
logger.logger.critical('This is a critical message')
4. Pytest框架中的Logging实战
pytest日志的配置 - pytest.ini
1、pytest 是有日志的开关: pytest.ini
不能改名字
不要在文件里面里面写中文
控制显示的格式及是否显示
日志配置代码 :
[pytest]
log_cli= true
log_level=NOTSET
log_format = %(asctime)s %(levelname)s %(message)s %(filename)s %(funcName)s %(lineno)d
log_date_format = %Y-%m-%d %H:%M:%S
log_file = ./logdata/log.log
log_file_level = info
log_file_format = %(asctime)s %(levelname)s %(message)s %(filename)s %(funcName)s %(lineno)d
log_file_date_format = %Y-%m-%d %H:%M:%S
代码解释:
测试夹具配置 - conftest:
2. 每个测试用例都需要产生对应的日志信息:
● 测试夹具(固定的写法)、控制显示哪些字段
● yield 方法:它就是一个返回的作用,类似于return;
● 区别: return返回之后就直接回到调用的地方;
● vield:调用执行完毕之后会回到调用的地方(vield)
这里基于上面的conftest代码做修改,如下:
# file name:conftest.py(文件名固定,不能修改)
import pytest
import logging
from Requests_demo.day06.PytestFrame.api_keyword.api_key import ApiKey
from Requests_demo.day06.PytestFrame.config import *
@pytest.fixture(scope= "session")
def token_fix():
ak = ApiKey()
url = PROJECT_URL+"?s=/api/user/login"
pulic_data = PULIC_DATA
data = {"accounts": USERNAME, "pwd": PASSWORD, "type": LOGINTYPE}
print("USERNAME的值:",USERNAME)
res = ak.post(url=url, params=pulic_data, data=data)
token = ak.get_text(res.json(), "$..token")
return token, ak
# 当执行一个case的时候会自动的调用这个方法:把对应的数据传过来给到call
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_makereport(item, call):
# 通过 out = yield 定义了一个生成器。在生成器中,res = out.get_result() 获取了测试结果对象。
out = yield
res = out.get_result()
# res.when == "call":表示正在运行调用测试函数的阶段。
if res.when == "call":
logging.info(f"用例ID:{res.nodeid}")
logging.info(f"测试结果:{res.outcome}")
logging.info(f"故障表示:{res.longrepr}")
logging.info(f"异常:{call.excinfo}")
logging.info(f"用例耗时:{res.duration}")
logging.info("**************************************")
日志效果展示
成功的用例:
pytest.ini、conftest这两个文件配置完之后
重新来运行一下test_ds-04.py文件(即上面'项目级token多文件关联的代码'):
在当前项目文件下生成了一个logdata文件,记录了本次执行的详细日志、控制台也输出了详细的日志
失败的用例:
然后重新执行,控制台信息较多,我们直接看日志: