目录
📌 1. 变量与数据类型
📌 2. 条件语句
📌 3. 循环
for 循环
while 循环
📌 4. 函数定义和调用
📌 5. 列表推导式
📌 6. 字典操作
📌 7. 异常处理
📌 8. 类和对象
📌 9. 文件读写
📌 10. 导入模块
📌 11. 最简单的装饰器
装饰器带参数的函数
带参数的装饰器(多一层函数)
保留原函数签名(functools.wraps)
常见用途场景
🧪 练习题(可选
重试装饰器(带参数)
使用示例:接口请求失败自动重试
结合接口响应值判断是否需要重试
将重试装饰器模块化(utils/retry_decorator.py)
在项目中使用(结合 requests)
添加日志配置(可选)
📌 12.正则表达式
基本用法示例
1. 匹配字符串
2. 提取所有匹配项(返回列表)
3. 替换内容
4. 分割字符串
常用正则规则
接口测试中常见用法
1. 从响应中提取 email
2. 校验 token 格式(如:32位十六进制)
3. 从接口响应中提取用户 ID
测试技巧:调试正则的好工具
进阶(可选)
正则工具模块实例:
💡 基础练习
1. 提取 email
2. 提取 JSON 字段值
3. 验证手机号和邮箱合法性
4. 替换敏感信息
🎯 进阶挑战题(可选)
📌13模块封装
请求封装 + 断言封装模块(完整示例)
目录结构建议更新
请求封装模块:utils/request_handler.py
📝 配合 config/settings.yaml:
断言封装模块:utils/assert_handler.py
用例中这样使用(示例)
扩展建议(你可选)
封装实例
封装建议结构(结合你目前的项目)
实战封装模块内容
1. utils/request_handler.py
封装统一的 GET/POST 请求:
2. utils/retry_decorator.py
如之前讲过的重试机制装饰器。
3. utils/regex_utils.py
4. utils/assert_handler.py ✅ 推荐封装
5. 示例用例中调用(testcases/test_user_api.py)
封装优势
你可以继续封装的模块(可选)
📌14 用例参数化
目录结构建议
data/user_data.yaml 示例
utils/data_loader.py 读取 YAML 工具
测试用例中使用 pytest.mark.parametrize
运行结果
📌15类继承(Class Inheritance)
什么是继承?
基本语法
构造函数继承(super())
重写方法(Override)
实际例子:接口请求类继承
继承结构图(层级)
注意点和实战建议
📌16异步
✅ 1. 常见异步函数示例(按用途)
📡 网络请求类(IO密集)
🧪 接口测试类(自动化/并发)
🕸 网络爬虫类
🗂 文件操作类(aiofiles)
✅ 2. 常用异步库及其函数
✅ 3. asyncio 提供的常用异步函数
为什么用异步?
使用异步请求:httpx + asyncio
安装:
最小示例:异步请求多个接口
如何应用到接口测试?
如何与 Pytest 结合使用(推荐)
异步的核心语法说明
异步 vs 多线程
项目结构(异步支持版)
安装依赖
异步请求封装模块:utils/async_request_handler.py
YAML 数据驱动文件:data/user_data.yaml
数据读取工具:utils/data_loader.py
异步测试用例:testcases/test_async_user.py
运行测试
📌17 Allure
一、报告增强 + 失败用例截图
Allure 报告增强的关键点
用例示例:
增强失败记录 + attach 内容到报告
运行用例并生成 Allure 报告
截图模拟(接口测试如何“截图”?)
效果展示(在 Allure 报告中你将看到):
举个例子(pytest + allure):
📌18 Jenkins
一、Jenkins 生成 Allure 报告
一、环境准备
✅ Jenkins 必须安装插件:
✅ 二、项目目录结构建议
✅ 三、在项目中添加 Allure 配置(Pytest)
✅ 四、Jenkinsfile(核心)
五、Jenkins 中配置 Job
✅ 方法 1:基于 Jenkinsfile 创建 “Pipeline 项目”
✅ 方法 2:传统 Freestyle 项目(更简单)
✅ 六、成功执行后效果
二、Jenkins + Allure 报告 添加 邮件或飞书通知
📧 一、邮件通知配置(Jenkins)
✅ 步骤 1:安装邮件插件
✅ 步骤 2:配置系统邮件发送信息
✅ 步骤 3:Job 中添加构建后操作
💬 二、飞书通知(推荐,适合团队 IM 通知)
✅ 步骤 1:飞书创建机器人
✅ 步骤 2:Jenkins 中添加构建后 Shell 通知脚本
✅ 脚本内容:utils/notify_feishu.py
✅ 三、Allure 报告作为附件(可选)
📌 1. 变量与数据类型
x = 10 # 整数 int
y = 3.14 # 浮点 float
name = "Alice" # 字符串 str
is_ok = True # 布尔 bool
items = [1, 2, 3] # 列表 list
info = {"a": 1} # 字典 dict
📌 2. 条件语句
if x > 5:
print("大于 5")
elif x == 5:
print("等于 5")
else:
print("小于 5")
📌 3. 循环
for 循环
for i in range(3):
print(i) # 输出 0, 1, 2
while 循环
count = 0
while count < 3:
print(count)
count += 1
📌 4. 函数定义和调用
def add(a, b):
return a + b
result = add(3, 5)
print(result) # 输出 8
📌 5. 列表推导式
squares = [x**2 for x in range(5)]
# [0, 1, 4, 9, 16]
📌 6. 字典操作
person = {"name": "Alice", "age": 30}
print(person["name"]) # 访问
person["gender"] = "女" # 添加
del person["age"] # 删除
📌 7. 异常处理
try:
result = 10 / 0
except ZeroDivisionError:
print("除以零错误")
finally:
print("无论如何都会执行")
📌 8. 类和对象
class Person:
def __init__(self, name):
self.name = name
def greet(self):
print(f"你好,我是 {self.name}")
p = Person("张三")
p.greet() # 输出:你好,我是 张三
📌 9. 文件读写
# 写文件
with open("test.txt", "w") as f:
f.write("Hello, world!")
# 读文件
with open("test.txt", "r") as f:
content = f.read()
print(content)
📌 10. 导入模块
import math
print(math.sqrt(16)) # 输出 4.0
from datetime import datetime
print(datetime.now())
📌 11. 最简单的装饰器
def my_decorator(func):
def wrapper():
print("开始执行")
func()
print("执行结束")
return wrapper
@my_decorator # 等同于 hello = my_decorator(hello)
def hello():
print("我是被装饰的函数")
hello()
输出:
开始执行 我是被装饰的函数 执行结束
装饰器带参数的函数
def logger(func):
def wrapper(*args, **kwargs):
print(f"调用函数 {func.__name__},参数是 {args} {kwargs}")
return func(*args, **kwargs)
return wrapper
@logger
def add(a, b):
return a + b
print(add(3, 5))
输出:
调用函数 add,参数是 (3, 5) {} 8
带参数的装饰器(多一层函数)
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def greet():
print("Hi!")
greet()
输出:
Hi! Hi! Hi!
保留原函数签名(functools.wraps)
from functools import wraps
def logger(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Logging...")
return func(*args, **kwargs)
return wrapper
@logger
def foo():
"""这是原函数的文档"""
pass
print(foo.__name__) # foo,而不是 wrapper
print(foo.__doc__) # 这是原函数的文档
常见用途场景
| 用途 | 示例 |
|---|---|
| 日志记录 | @log |
| 性能统计 | @timeit |
| 权限验证 | @login_required(Web 开发中常用) |
| 缓存结果 | @lru_cache from functools |
| 接口签名校验 | @check_signature |
🧪 练习题(可选
# 实现一个装饰器:打印函数运行前后时间
import time
def timing(func):
# 补充代码
pass
@timing
def slow():
time.sleep(2)
print("函数执行完毕")
slow()
重试装饰器(带参数)
import time
from functools import wraps
def retry(retries=3, delay=2, allowed_exceptions=(Exception,), retry_condition=None):
"""
自动重试装饰器
:param retries: 最大重试次数
:param delay: 每次重试之间的延迟(秒)
:param allowed_exceptions: 允许重试的异常类型
:param retry_condition: 可选,函数返回值为 False 也可触发重试
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
attempt = 0
while attempt < retries:
try:
result = func(*args, **kwargs)
if retry_condition and not retry_condition(result):
raise ValueError("返回值不满足条件,重试")
return result
except allowed_exceptions as e:
attempt += 1
print(f"[Retry] 第 {attempt} 次尝试失败:{e}")
if attempt < retries:
time.sleep(delay)
else:
print("[Retry] 到达最大重试次数,放弃")
raise
return wrapper
return decorator
使用示例:接口请求失败自动重试
import requests
@retry(retries=3, delay=1, allowed_exceptions=(requests.exceptions.RequestException,))
def get_user_info():
res = requests.get("https://httpbin.org/status/500") # 故意失败
res.raise_for_status()
return res.json()
get_user_info()
输出类似:
[Retry] 第 1 次尝试失败:500 Server Error...
[Retry] 第 2 次尝试失败:500 Server Error...
[Retry] 第 3 次尝试失败:500 Server Error...
[Retry] 到达最大重试次数,放弃
结合接口响应值判断是否需要重试
比如有的接口返回 HTTP 200,但业务层返回 meta.code != 200,也需要重试:
@retry(
retries=3,
delay=2,
retry_condition=lambda res: res.get("meta", {}).get("code") == 200
)
def call_api():
return {
"meta": {"code": 500, "msg": "系统繁忙"}
}
call_api()
可以这样应用于自动化测试用例中:
@retry(retries=3, delay=1)
def send_request_with_retry():
response = requests.get("https://your-api.com/api/v1/users/me")
response.raise_for_status()
return response.json()
def test_user_api():
res = send_request_with_retry()
assert res['meta']['code'] == 200
将重试装饰器模块化(utils/retry_decorator.py)
你可以在项目目录下新建一个模块,代码如下:
# utils/retry_decorator.py
import time
import logging
from functools import wraps
logger = logging.getLogger(__name__)
def retry(retries=3, delay=2, allowed_exceptions=(Exception,), retry_condition=None, backoff=False):
"""
通用重试装饰器(支持异常和返回值条件重试)
:param retries: 最大重试次数
:param delay: 首次重试延迟(秒)
:param allowed_exceptions: 可触发重试的异常类型
:param retry_condition: 若返回值不满足条件,也触发重试(应为一个返回 True 表示成功的函数)
:param backoff: 是否启用指数回退(delay -> delay*2 -> delay*4)
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
wait = delay
for attempt in range(1, retries + 1):
try:
result = func(*args, **kwargs)
if retry_condition and not retry_condition(result):
raise ValueError("返回值不满足条件")
return result
except allowed_exceptions as e:
logger.warning(f"[Retry] 第 {attempt} 次失败:{e}")
if attempt == retries:
logger.error("[Retry] 重试次数用尽,抛出异常")
raise
else:
time.sleep(wait)
if backoff:
wait *= 2
return wrapper
return decorator
在项目中使用(结合 requests)
你可以在 testcases/test_user_api.py 中使用这个装饰器:
from utils.retry_decorator import retry
from utils.request_handler import RequestHandler
rh = RequestHandler()
@retry(
retries=3,
delay=1,
allowed_exceptions=(Exception,),
retry_condition=lambda res: res["meta"]["code"] == 200,
backoff=True
)
def get_user_info():
response = rh.get("/api/v1/users/me")
response.raise_for_status()
return response.json()
def test_get_user_info():
res = get_user_info()
assert res["meta"]["code"] == 200
assert "email" in res["data"]
添加日志配置(可选)
在项目根目录下新建 logging_config.py(或者在 conftest.py 里配置):
# logging_config.py
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s"
)
在测试前导入它即可:
# conftest.py
import logging_config
📌 12.正则表达式
Python 的 正则表达式(re模块),它是处理字符串匹配、提取、替换等任务的利器,常用于接口测试中的:
提取 token、id、email、code
校验响应内容格式
日志解析
断言字段值
基本用法示例
1. 匹配字符串
text = "My email is test@example.com"
match = re.search(r"w+@w+.w+", text)
print(match.group()) # 输出:test@example.com
2. 提取所有匹配项(返回列表)
text = "Phones: 123-4567, 890-1234"
matches = re.findall(r"d{3}-d{4}", text)
print(matches) # ['123-4567', '890-1234']
3. 替换内容
text = "Hello 123, world 456"
new_text = re.sub(r"d+", "***", text)
print(new_text) # Hello ***, world ***
4. 分割字符串
text = "one,two;three four"
result = re.split(r"[,s;]+", text)
print(result) # ['one', 'two', 'three', 'four']
常用正则规则
| 表达式 | 含义 | 示例匹配 |
|---|---|---|
d |
数字(0-9) | 123 |
w |
字符(数字、字母、下划线) | abc_123 |
. |
任意字符(不含换行) | a, b, 1 |
* |
前一个出现 0 次或多次 | ab* -> a, ab, abb |
+ |
前一个出现 1 次或多次 | ab+ -> ab, abb |
? |
前一个出现 0 或 1 次 | ab? -> a, ab |
^ |
开头 | ^abc |
$ |
结尾 | xyz$ |
[...] |
匹配集合 | [abc] -> a 或 b |
[^...] |
不匹配集合中的任意一个 | [^abc] |
( ) |
分组 | (d+)-(d+) |
| ` | ` | 或 |
接口测试中常见用法
1. 从响应中提取 email
import re
resp_text = '{"email": "user123@example.com"}'
email = re.search(r'"email":s*"([^"]+)"', resp_text).group(1)
print(email) # user123@example.com
2. 校验 token 格式(如:32位十六进制)
token = "a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3"
assert re.fullmatch(r"[a-f0-9]{32}", token)
3. 从接口响应中提取用户 ID
text = '{"id": 12345, "name": "Alice"}'
match = re.search(r'"id":s*(d+)', text)
if match:
user_id = int(match.group(1))
print(user_id) # 12345
测试技巧:调试正则的好工具
在线正则调试工具推荐:
regex101.com(强烈推荐)
regexr.com
VSCode + Regex Previewer 插件
进阶(可选)
| 功能 | 示例 |
|---|---|
| 命名分组 | (?P<name>w+),用 m.group("name") 获取 |
| 非贪婪匹配 | .+? 而不是 .+ |
| 多行匹配 | re.M 或 (?m) |
| 匹配 JSON 中嵌套字段 | `r'”status”:s*”(ok |
正则工具模块实例:
utils/regex_utils.py
你可以将下面的代码保存为 utils/regex_utils.py,方便你在测试代码中直接调用。
import re
def extract_email(text):
"""从文本中提取第一个 email"""
match = re.search(r"[w.-]+@[w.-]+.w+", text)
return match.group() if match else None
def extract_field(text, field):
"""从 JSON 字符串中提取指定字段值(仅支持简单 key)"""
pattern = fr'"{field}":s*"?(.*?)"?[,}}]'
match = re.search(pattern, text)
return match.group(1) if match else None
def is_valid_email(email):
"""验证 email 格式"""
return re.fullmatch(r"[w.-]+@[w.-]+.w+", email) is not None
def is_valid_phone(phone):
"""验证手机号格式(支持中国 +86)"""
return re.fullmatch(r"1[3-9]d{9}", phone) is not None
def mask_sensitive(text):
"""替换 email、手机号中的中间部分为 ***"""
text = re.sub(r"([w.-]+)@([w.-]+)", r"1***@2", text)
text = re.sub(r"(1[3-9])d{4}(d{4})", r"1****2", text)
return text
💡 基础练习
1. 提取 email
文本:"用户邮箱为:user_01@example.com"
text = "用户邮箱为:user_01@example.com"
print(extract_email(text)) # user_01@example.com
2. 提取 JSON 字段值
JSON:{"token": "abc123xyz", "user_id": 789}
json_str = '{"token": "abc123xyz", "user_id": 789}'
print(extract_field(json_str, "token")) # abc123xyz
print(extract_field(json_str, "user_id")) # 789
3. 验证手机号和邮箱合法性
print(is_valid_email("user@qq.com")) # True
print(is_valid_phone("13812345678")) # True
print(is_valid_phone("188123")) # False
4. 替换敏感信息
text = "手机号:13812345678,邮箱:admin@site.com"
print(mask_sensitive(text))
# 输出:手机号:138****5678,邮箱:admin***@site.com
🎯 进阶挑战题(可选)
你可以尝试用正则完成下面需求:
匹配字符串 "status": "ok" 或 "status": "error"
提取多组 email(用 re.findall)
从 URL 提取 query 参数中的 token 值,例如:
https://example.com/api?token=abc123&user=1
📌13模块封装
请求封装 + 断言封装模块(完整示例)
目录结构建议更新
api_test_project/
├── utils/
│ ├── request_handler.py ✅ 请求封装
│ ├── assert_handler.py ✅ 断言封装
请求封装模块:utils/request_handler.py
import requests
import yaml
import os
class RequestHandler:
def __init__(self):
self.config = self._load_config()
self.base_url = self.config.get("base_url", "")
self.headers = self.config.get("headers", {})
def _load_config(self):
path = os.path.join(os.path.dirname(__file__), "../config/settings.yaml")
with open(path, "r", encoding="utf-8") as f:
return yaml.safe_load(f)
def get(self, path, params=None):
url = self.base_url + path
return requests.get(url, headers=self.headers, params=params)
def post(self, path, json=None):
url = self.base_url + path
return requests.post(url, headers=self.headers, json=json)
def put(self, path, json=None):
url = self.base_url + path
return requests.put(url, headers=self.headers, json=json)
def delete(self, path, json=None):
url = self.base_url + path
return requests.delete(url, headers=self.headers, json=json)
📝 配合 config/settings.yaml:
base_url: "https://your-api.com"
headers:
Accept: "application/json"
断言封装模块:utils/assert_handler.py
def assert_status_code(response, expected_code):
"""断言响应状态码"""
actual = response.status_code
assert actual == expected_code, f"状态码断言失败:期望 {expected_code},实际 {actual}"
def assert_json_value(json_data, field_path, expected_value):
"""
支持用 'a.b.c' 方式访问嵌套字段
"""
keys = field_path.split(".")
actual = json_data
try:
for key in keys:
actual = actual[key]
except Exception:
raise AssertionError(f"字段路径无效:{field_path}")
assert actual == expected_value, f"字段 '{field_path}' 值断言失败:期望 {expected_value},实际 {actual}"
def assert_key_exists(json_data, key):
"""断言 JSON 字段存在"""
assert key in json_data, f"字段 '{key}' 不存在"
用例中这样使用(示例)
from utils.request_handler import RequestHandler
from utils.assert_handler import (
assert_status_code,
assert_json_value,
assert_key_exists
)
rh = RequestHandler()
def test_get_user_info():
res = rh.get("/api/v1/users/me")
res_json = res.json()
assert_status_code(res, 200)
assert_json_value(res_json, "meta.code", 200)
assert_key_exists(res_json["data"], "email")
扩展建议(你可选)
| 模块功能 | 建议实现方式 |
|---|---|
| ✅ 响应时间断言 | assert response.elapsed.total_seconds() < 1 |
| ✅ 支持 schema 校验 | 使用 jsonschema 断言结构 |
| ✅ Allure 步骤封装 | 所有断言封装中加 allure.step |
| ✅ 失败截图/日志 | 断言失败时自动截图/记录日志(Web) |
封装实例
封装建议结构(结合你目前的项目)
api_test_project/ ├── config/ │ └── settings.yaml # 配置项(url、token) ├── data/ │ └── test_data.yaml # 测试数据 ├── testcases/ │ └── test_user_api.py # 测试用例 ├── utils/ │ ├── request_handler.py # 请求封装 │ ├── retry_decorator.py # 重试封装 │ ├── regex_utils.py # 正则封装 │ ├── assert_handler.py # 断言封装(推荐) │ └── logger.py # 日志封装(可选) ├── conftest.py # Pytest hooks / fixtures ├── pytest.ini └── requirements.txt
实战封装模块内容
1. utils/request_handler.py
封装统一的 GET/POST 请求:
import requests
import yaml
import os
class RequestHandler:
def __init__(self):
self.base_url, self.headers = self._load_config()
def _load_config(self):
path = os.path.join(os.path.dirname(__file__), '../config/settings.yaml')
with open(path, 'r', encoding='utf-8') as f:
cfg = yaml.safe_load(f)
return cfg['base_url'], cfg['headers']
def get(self, path, params=None):
url = self.base_url + path
return requests.get(url, headers=self.headers, params=params)
def post(self, path, json=None):
url = self.base_url + path
return requests.post(url, headers=self.headers, json=json)
2. utils/retry_decorator.py
如之前讲过的重试机制装饰器。
3. utils/regex_utils.py
正则处理工具函数模块,供提取字段、校验使用。
4. utils/assert_handler.py ✅ 推荐封装
def assert_status_code(response, expected_code):
assert response.status_code == expected_code, f"预期状态码 {expected_code},实际为 {response.status_code}"
def assert_json_field(json_data, key, expected_value):
actual_value = json_data.get(key)
assert actual_value == expected_value, f"{key} 字段断言失败,预期:{expected_value},实际:{actual_value}"
5. 示例用例中调用(testcases/test_user_api.py)
from utils.request_handler import RequestHandler
from utils.retry_decorator import retry
from utils.assert_handler import assert_status_code, assert_json_field
from utils.regex_utils import extract_email
rh = RequestHandler()
@retry(retries=3)
def send_user_me_request():
return rh.get("/api/v1/users/me")
def test_get_user_info():
resp = send_user_me_request()
json_data = resp.json()
assert_status_code(resp, 200)
assert_json_field(json_data['meta'], 'code', 200)
assert "email" in json_data['data']
assert extract_email(json_data['data']['email']) # 正则校验
封装优势
| 优势 | 举例说明 |
|---|---|
| ✨ 代码复用 | 一个 request_handler 多处可用 |
| ✨ 解耦易维护 | 改请求只改模块,不影响用例逻辑 |
| ✨ 更强的组织结构 | 遵循“单一职责原则” |
| ✨ 易扩展 | 加代理、token、日志都能集中管理 |
你可以继续封装的模块(可选)
| 模块名 | 功能 |
|---|---|
token_handler.py |
登录并自动获取 token |
env_switcher.py |
根据环境切换 base_url/test_url |
db_handler.py |
数据库连接和查询 |
report_utils.py |
对 Allure 报告做增强,比如截图或备注 |
📌14 用例参数化
目录结构建议
api_test_project/ ├── data/ │ └── user_data.yaml # 存储参数化测试数据 ├── testcases/ │ └── test_user_login.py # 参数化测试用例 ├── utils/ │ └── data_loader.py # 读取 yaml 数据工具
data/user_data.yaml 示例
login_cases:
- title: "正常登录"
input:
username: "user01"
password: "pass01"
expected:
code: 200
msg: "登录成功"
- title: "密码错误"
input:
username: "user01"
password: "wrongpass"
expected:
code: 401
msg: "密码错误"
- title: "用户不存在"
input:
username: "nosuchuser"
password: "any"
expected:
code: 404
msg: "用户不存在"
utils/data_loader.py 读取 YAML 工具
import yaml
import os
def load_yaml(path):
abs_path = os.path.join(os.path.dirname(__file__), '..', path)
with open(abs_path, 'r', encoding='utf-8') as f:
return yaml.safe_load(f)
测试用例中使用 pytest.mark.parametrize
import pytest
from utils.data_loader import load_yaml
from utils.request_handler import RequestHandler
rh = RequestHandler()
login_data = load_yaml('data/user_data.yaml')['login_cases']
@pytest.mark.parametrize("case", login_data, ids=[i['title'] for i in login_data])
def test_login(case):
path = "/api/v1/login"
payload = case['input']
expected = case['expected']
res = rh.post(path, json=payload).json()
assert res["code"] == expected["code"]
assert res["msg"] == expected["msg"]
运行结果
执行:
pytest testcases/test_user_login.py -v
输出示例:
test_user_login.py::test_login[正常登录] PASSED
test_user_login.py::test_login[密码错误] PASSED
test_user_login.py::test_login[用户不存在] PASSED
进阶建议(可选扩展)
| 功能 | 示例 |
|---|---|
| ❗ 参数组合覆盖(笛卡尔积) | pytest.mark.parametrize 嵌套 |
| ✅ Excel 支持 | 用 openpyxl 或 pandas 加载 |
| ✅ JSON 数据源 | 使用 json.load() |
| ✅ 参数动态生成 | 如生成随机手机号、邮箱等 |
| ✅ 与 token/session 联动 | 登录态测试动态处理 token |
📌15类继承(Class Inheritance)
什么是继承?
就是一个 类(子类)继承另一个类(父类)的方法和属性,可以在不重复代码的前提下扩展或修改功能。
基本语法
class 父类:
def 方法1(self):
print("父类方法")
class 子类(父类): # 子类继承父类
def 方法2(self):
print("子类方法")
obj = 子类()
obj.方法1() # 来自父类
obj.方法2() # 来自子类
输出:
父类方法 子类方法
构造函数继承(super())
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print(f"{self.name} 会叫")
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # 调用父类构造器
self.breed = breed
def speak(self):
print(f"{self.name} 是一只 {self.breed},汪汪汪!")
dog = Dog("旺财", "柴犬")
dog.speak()
输出:
旺财 是一只 柴犬,汪汪汪!
重写方法(Override)
子类可以 重写 父类的方法来自定义逻辑:
class Animal:
def sound(self):
print("发出声音")
class Cat(Animal):
def sound(self):
print("喵喵喵")
Cat().sound() # 输出:喵喵喵
# 输出:喵喵喵
实际例子:接口请求类继承
# 基础请求类
class BaseRequest:
def __init__(self, base_url):
self.base_url = base_url
def get_full_url(self, path):
return self.base_url + path
# 用户接口请求类
class UserAPI(BaseRequest):
def get_me(self):
url = self.get_full_url("/api/v1/users/me")
return requests.get(url).json()
# 管理员接口请求类
class AdminAPI(BaseRequest):
def get_users(self):
url = self.get_full_url("/api/v1/users")
return requests.get(url).json()
# 使用
user_api = UserAPI("https://example.com")
print(user_api.get_me())
继承结构图(层级)
┌────────────┐ │ BaseAPI │ └─────┬──────┘ │ ┌────────┴────────┐ │ │ UserAPI AdminAPI
注意点和实战建议
| 建议 | 示例/说明 |
|---|---|
尽量用 super() 调用父类 |
保证父类逻辑不被丢掉 |
| 父类写公共功能 | 如 get/post, headers, token等 |
| 子类写业务接口逻辑 | 如 get_user_info, delete_user() |
| 支持多态、动态绑定 | 不同子类实现相同接口逻辑 |
📌16异步
✅ 1. 常见异步函数示例(按用途)
📡 网络请求类(IO密集)
| 场景 | 示例异步函数 |
|---|---|
| HTTP请求 | httpx.AsyncClient.get(), aiohttp.ClientSession.get() |
| WebSocket | websockets.connect() |
| 下载文件 | aiofiles.open() 写入异步文件 |
| 接口测试 | 自定义异步封装如:async def test_api(): await client.get() |
🧪 接口测试类(自动化/并发)
import httpx
async def fetch_user():
async with httpx.AsyncClient() as client:
resp = await client.get("https://api.example.com/users")
return resp.json()
🕸 网络爬虫类
import aiohttp
async def crawl(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
🗂 文件操作类(aiofiles)
import aiofiles
async def write_log():
async with aiofiles.open('log.txt', 'w') as f:
await f.write('Async log
')
✅ 2. 常用异步库及其函数
| 库名 | 异步函数示例 | 用途 |
|---|---|---|
asyncio |
asyncio.sleep(), asyncio.gather() |
核心异步工具 |
aiohttp |
ClientSession.get() |
异步 HTTP 请求 |
httpx |
AsyncClient.get() |
现代异步 HTTP 客户端 |
aiofiles |
aiofiles.open() |
异步文件读写 |
websockets |
websockets.connect() |
异步 WebSocket 通信 |
databases |
database.fetch_all() |
异步数据库访问 |
aiomysql / asyncpg |
conn.fetch(), conn.execute() |
异步数据库操作 |
✅ 3. asyncio 提供的常用异步函数
asyncio.sleep()
asyncio.gather()
asyncio.create_task()
asyncio.wait()
asyncio.as_completed()
为什么用异步?
普通的 requests 是同步阻塞的:
res1 = requests.get(...)
res2 = requests.get(...)
要等 res1 完了,才能发 res2。
而用异步,可以并发执行多个请求,大大提速,尤其在 IO 密集场景(如接口测试、文件操作)中。
使用异步请求:httpx + asyncio
安装:
pip install httpx
最小示例:异步请求多个接口
import httpx
import asyncio
async def fetch_user(client, user_id):
url = f"https://jsonplaceholder.typicode.com/users/{user_id}"
resp = await client.get(url)
return resp.json()
async def main():
async with httpx.AsyncClient() as client:
tasks = [fetch_user(client, i) for i in range(1, 6)]
results = await asyncio.gather(*tasks)
for user in results:
print(user['name'])
asyncio.run(main())
输出(并发获取 5 个用户):
Leanne Graham
Ervin Howell
Clementine Bauch
...
如何应用到接口测试?
import httpx
import asyncio
test_cases = [
{"path": "/api/v1/users/me", "expected_code": 200},
{"path": "/api/v1/settings", "expected_code": 200},
]
BASE_URL = "https://your-api.com"
async def test_case(client, case):
url = BASE_URL + case["path"]
resp = await client.get(url)
print(f"[{case['path']}] Status: {resp.status_code}")
assert resp.status_code == case["expected_code"]
async def run_all_tests():
async with httpx.AsyncClient() as client:
tasks = [test_case(client, case) for case in test_cases]
await asyncio.gather(*tasks)
asyncio.run(run_all_tests())
如何与 Pytest 结合使用(推荐)
安装:
pip install pytest-asyncio
用法:
import pytest
import httpx
@pytest.mark.asyncio
async def test_async_user():
async with httpx.AsyncClient() as client:
res = await client.get("https://jsonplaceholder.typicode.com/users/1")
assert res.status_code == 200
assert res.json()["id"] == 1
异步的核心语法说明
| 关键字 | 含义 |
|---|---|
async def |
定义协程函数 |
await |
等待异步操作完成 |
asyncio.run() |
启动事件循环 |
async with |
异步上下文管理器(如客户端) |
asyncio.gather |
并发执行多个异步任务 |
异步 vs 多线程
| 比较项 | 异步(asyncio + httpx) | 多线程(threading + requests) |
|---|---|---|
| 并发性能 | 优秀,轻量,适合 IO 操作 | 较重,切换成本高 |
| 易用性 | 较陡峭,有 async/await 限制 | 易用,但阻塞 |
| 推荐场景 | 批量接口请求、爬虫、高性能测试 | 调试/小型任务 |
项目结构(异步支持版)
api_test_project/
├── testcases/
│ └── test_async_user.py # 异步测试用例
├── utils/
│ ├── async_request_handler.py # 异步请求封装
│ └── data_loader.py # 参数化数据读取
├── data/
│ └── user_data.yaml # 测试数据
├── conftest.py # pytest 全局配置
└── requirements.txt
安装依赖
pip install httpx pytest pytest-asyncio pyyaml
requirements.txt 示例:
httpx
pytest
pytest-asyncio
pyyaml
异步请求封装模块:utils/async_request_handler.py
import httpx
import yaml
import os
class AsyncRequestHandler:
def __init__(self):
self.base_url, self.headers = self._load_config()
def _load_config(self):
path = os.path.join(os.path.dirname(__file__), '../config/settings.yaml')
with open(path, 'r', encoding='utf-8') as f:
cfg = yaml.safe_load(f)
return cfg['base_url'], cfg.get('headers', {})
async def get(self, path, params=None):
async with httpx.AsyncClient() as client:
resp = await client.get(self.base_url + path, params=params, headers=self.headers)
return resp
async def post(self, path, json=None):
async with httpx.AsyncClient() as client:
resp = await client.post(self.base_url + path, json=json, headers=self.headers)
return resp
YAML 数据驱动文件:data/user_data.yaml
async_cases:
- title: "获取当前用户信息"
path: "/api/v1/users/me"
method: "get"
expected_code: 200
- title: "获取系统设置"
path: "/api/v1/settings"
method: "get"
expected_code: 200
数据读取工具:utils/data_loader.py
import yaml
import os
def load_yaml(path):
abs_path = os.path.join(os.path.dirname(__file__), '..', path)
with open(abs_path, 'r', encoding='utf-8') as f:
return yaml.safe_load(f)
异步测试用例:testcases/test_async_user.py
import pytest
from utils.data_loader import load_yaml
from utils.async_request_handler import AsyncRequestHandler
rh = AsyncRequestHandler()
case_data = load_yaml('data/user_data.yaml')['async_cases']
@pytest.mark.asyncio
@pytest.mark.parametrize("case", case_data, ids=[i['title'] for i in case_data])
async def test_async_api(case):
method = case["method"]
path = case["path"]
expected_code = case["expected_code"]
if method == "get":
resp = await rh.get(path)
elif method == "post":
resp = await rh.post(path)
else:
raise ValueError(f"不支持的请求方法: {method}")
assert resp.status_code == expected_code, f"预期 {expected_code},实际 {resp.status_code}"
运行测试
pytest testcases/test_async_user.py -v
输出示例:
test_async_user.py::test_async_api[获取当前用户信息] PASSED
test_async_user.py::test_async_api[获取系统设置] PASSED
📌17 Allure
一、报告增强 + 失败用例截图
Allure 报告增强的关键点
| 功能 | 实现方式 |
|---|---|
| ✅ 添加步骤与描述 | 使用 @allure.step 和 allure.attach() |
| ✅ 失败时附上响应正文 | 捕获 response.text 写入 Allure |
| ✅ 附加请求/响应截图(文本) | 把 JSON 写入 .txt 再 attach |
用例示例:
增强失败记录 + attach 内容到报告
import pytest
import allure
from utils.request_handler import RequestHandler
from utils.assert_handler import assert_status_code
import json
rh = RequestHandler()
@allure.feature("用户模块")
@allure.story("获取当前用户信息")
@allure.title("获取 /users/me 接口信息")
def test_get_user_info():
path = "/api/v1/users/me"
with allure.step(f"发送 GET 请求: {path}"):
res = rh.get(path)
allure.attach(
body=res.text,
name="响应正文",
attachment_type=allure.attachment_type.JSON
)
with allure.step("断言响应状态码为 200"):
try:
assert_status_code(res, 200)
except AssertionError as e:
allure.attach(
body=json.dumps({
"status_code": res.status_code,
"url": res.url,
"headers": dict(res.headers),
"body": res.text
}, indent=2, ensure_ascii=False),
name="失败调试信息",
attachment_type=allure.attachment_type.TEXT
)
raise
运行用例并生成 Allure 报告
pytest --alluredir=./report
allure generate ./report -o ./report/html --clean
allure open ./report/html
截图模拟(接口测试如何“截图”?)
虽然接口测试无法截图 UI,但你可以将错误信息或 JSON 响应内容“伪截图”保存:
allure.attach(
json.dumps(res.json(), indent=2, ensure_ascii=False),
name="响应截图模拟",
attachment_type=allure.attachment_type.TEXT
)
效果展示(在 Allure 报告中你将看到):
每个 step 详尽显示:接口路径、参数、预期
失败时,报告中会有:
❌ 错误断言截图(response.json)
🔍 headers、body、url 信息
所有内容都支持点击查看、复制
举个例子(pytest + allure):
import allure
@allure.feature("用户模块")
@allure.story("获取个人信息")
@allure.severity(allure.severity_level.CRITICAL)
def test_get_user_info():
with allure.step("发送 GET 请求"):
response = requests.get("https://api.example.com/user/me")
with allure.step("断言响应状态码"):
assert response.status_code == 200
📌18 Jenkins
一、Jenkins 生成 Allure 报告
一、环境准备
✅ Jenkins 必须安装插件:
Allure Jenkins Plugin
Pipeline Plugin
ShiningPanda Plugin(执行 Python 脚本) 或自己配置 Python 环境
插件路径:
Manage Jenkins → Plugin Manager → Available
✅ 二、项目目录结构建议
确保你的接口自动化项目中有:
api_test_project/ ├── testcases/ ├── utils/ ├── config/ ├── data/ ├── report/ │ └── allure-results/ # pytest 输出的原始数据 ├── requirements.txt ├── pytest.ini └── Jenkinsfile ✅ Jenkins 执行脚本(重点)
✅ 三、在项目中添加 Allure 配置(Pytest)
安装依赖:
pip install allure-pytest
运行时指定输出目录:
pytest --alluredir=report/allure-results
✅ 四、Jenkinsfile(核心)
在项目根目录创建 Jenkinsfile,内容如下:
pipeline {
agent any
tools {
python 'Python3' // 你配置好的 Python 版本名
}
environment {
ALLURE_RESULTS = "report/allure-results"
}
stages {
stage('Checkout') {
steps {
git 'https://your-repo-url.git'
}
}
stage('Install Dependencies') {
steps {
sh 'pip install -r requirements.txt'
}
}
stage('Run API Tests') {
steps {
sh 'pytest --alluredir=${ALLURE_RESULTS}'
}
}
stage('Generate Allure Report') {
steps {
allure([
includeProperties: false,
jdk: '',
results: [[path: "${ALLURE_RESULTS}"]]
])
}
}
}
post {
always {
archiveArtifacts artifacts: '**/allure-results/*.json', allowEmptyArchive: true
}
}
}
五、Jenkins 中配置 Job
✅ 方法 1:基于 Jenkinsfile 创建 “Pipeline 项目”
创建新任务 → 选择 Pipeline
配置 Git 仓库地址
确保选择 使用 Jenkinsfile(从 SCM)
保存 & 构建
✅ 方法 2:传统 Freestyle 项目(更简单)
创建新任务 → 选择 Freestyle
添加 Git 仓库地址
添加构建步骤 → 执行 Shell:
pip install -r requirements.txt pytest --alluredir=report/allure-results
添加构建后操作 → Allure Report
Path to results: report/allure-results
✅ 六、成功执行后效果
在 Jenkins 页面你将看到:
📊 Allure 报告按钮 ✅
✅ 总测试数量 / 通过 / 失败 / 跳过
🧪 每个接口详细的 step、响应、错误信息(你之前封装过的)
二、Jenkins + Allure 报告 添加 邮件或飞书通知
📧 一、邮件通知配置(Jenkins)
✅ 步骤 1:安装邮件插件
Jenkins 插件中心安装:
✅ Email Extension Plugin
(可选)Mail Watcher、Mailer
✅ 步骤 2:配置系统邮件发送信息
路径:Manage Jenkins → Configure System → Extended E-mail Notification
示例配置(以 Gmail 为例):
SMTP Server: smtp.gmail.com
Use SMTP Authentication: ☑️
Username: your_email@gmail.com
Password: App Password(不是登录密码)
SMTP Port: 465
Use SSL: ☑️
测试发送成功后保存。
✅ 步骤 3:Job 中添加构建后操作
在你的任务里:
点击 构建后操作 → Editable Email Notification
填写字段:
| 字段 | 示例 |
|---|---|
| Recipients | dev_team@example.com |
| Subject | 接口测试报告 - 构建 #${BUILD_NUMBER} |
| Content | ${BUILD_STATUS},点击查看报告:${BUILD_URL}allure/ |
触发条件:失败、始终、回归等可选
💬 二、飞书通知(推荐,适合团队 IM 通知)
✅ 步骤 1:飞书创建机器人
打开一个飞书群 → 群设置 → 添加机器人
添加 自定义机器人
设置名称,例如“自动化测试通知”
复制 Webhook 地址,形如:
https://open.feishu.cn/open-apis/bot/v2/hook/xxx
✅ 步骤 2:Jenkins 中添加构建后 Shell 通知脚本
你可以在 Jenkinsfile 或 Freestyle Job 的“构建后操作”中添加:
python utils/notify_feishu.py
✅ 脚本内容:utils/notify_feishu.py
import requests
import json
import os
WEBHOOK_URL = "https://open.feishu.cn/open-apis/bot/v2/hook/xxx"
build_number = os.getenv("BUILD_NUMBER", "N/A")
build_url = os.getenv("BUILD_URL", "#")
content = {
"msg_type": "text",
"content": {
"text": f"✅ 接口测试完成
构建号: #{build_number}
报告地址: {build_url}allure/"
}
}
response = requests.post(WEBHOOK_URL, headers={"Content-Type": "application/json"}, data=json.dumps(content))
print("飞书通知状态:", response.status_code)
如果你希望发送更多信息(失败数、耗时等),可以让测试框架生成 JSON,再读入发送。
✅ 三、Allure 报告作为附件(可选)
用 allure generate 生成 HTML 报告:
allure generate report/allure-results -o report/html --clean
用 zip 打包:
zip -r allure-report.zip report/html
作为邮件附件发送(需 SMTP 支持)
你只需部署 Jenkins → 配 webhook / 邮件 → 一键运行。


















暂无评论内容