Python语法教程

目录

📌 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 支持 openpyxlpandas 加载
✅ 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.stepallure.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 / 邮件 → 一键运行。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容