文章目录
- [14 Python接口自动化 - pytest测试框架](#14 Python接口自动化 - pytest测试框架)
-
- [14.1 什么是pytest](#14.1 什么是pytest)
-
- [pytest vs unittest](#pytest vs unittest)
- [14.2 pytest 基础](#14.2 pytest 基础)
- [14.3 夹具(Fixture)](#14.3 夹具(Fixture))
- [14.4 参数化测试](#14.4 参数化测试)
- [14.5 标记(Marker)](#14.5 标记(Marker))
- [14.6 钩子函数(Hook)](#14.6 钩子函数(Hook))
- [14.7 完整的接口自动化项目](#14.7 完整的接口自动化项目)
- [14.8 生成测试报告](#14.8 生成测试报告)
- [14.9 本章小结](#14.9 本章小结)
-
- pytest核心特性
- [课后练习 📝](#课后练习 📝)
- [14.10 下章预告](#14.10 下章预告)
14 Python接口自动化 - pytest测试框架
🎯 本章目标:掌握pytest测试框架,学会用pytest编写专业的接口自动化测试。
14.1 什么是pytest
pytest 是Python最流行的测试框架,简洁、强大、易用。
pytest vs unittest
| 特性 | pytest | unittest |
|---|---|---|
| 语法 | 简洁(assert) | 繁琐(self.assertEqual) |
| 插件 | 丰富 | 较少 |
| 参数化 | 内置支持 | 需额外实现 |
| 夹具(Fixture) | 强大 | setUp/tearDown |
| 社区 | 活跃 | 标准库 |
14.2 pytest 基础
第一个测试
python
# test_demo.py
def add(x, y):
return x + y
def test_add():
assert add(1, 2) == 3
def test_add_negative():
assert add(-1, -2) == -3
运行测试
bash
# 运行当前目录所有测试
pytest
# 运行指定文件
pytest test_demo.py
# 运行指定函数
pytest test_demo.py::test_add
# 显示详细信息
pytest -v
# 生成HTML报告
pytest --html=report.html
14.3 夹具(Fixture)
什么是Fixture
Fixture 是pytest的核心特性,用于准备测试环境(如创建数据库连接、初始化数据等)。
基础Fixture
python
import pytest
import requests
@pytest.fixture
def base_url():
"""提供基础URL"""
return "https://api.example.com"
@pytest.fixture
def session():
"""创建HTTP会话"""
s = requests.Session()
s.headers.update({"Content-Type": "application/json"})
yield s # yield前是setup,yield后是teardown
s.close()
@pytest.fixture
def auth_token(session, base_url):
"""获取认证token"""
response = session.post(f"{base_url}/api/v1/login", json={
"username": "admin",
"password": "123456"
})
return response.json()["data"]["token"]
# 使用Fixture
def test_get_user(session, base_url, auth_token):
session.headers.update({"Authorization": f"Bearer {auth_token}"})
response = session.get(f"{base_url}/api/v1/users/me")
assert response.status_code == 200
Fixture作用域
python
@pytest.fixture(scope="function") # 每个测试函数执行一次(默认)
@pytest.fixture(scope="class") # 每个测试类执行一次
@pytest.fixture(scope="module") # 每个模块执行一次
@pytest.fixture(scope="session") # 整个测试会话执行一次
14.4 参数化测试
基础参数化
python
import pytest
@pytest.mark.parametrize("username,password,expected", [
("admin", "123456", 0),
("admin", "wrong", 1001),
("", "123456", 1002),
("test", "123", 1003),
])
def test_login(username, password, expected):
"""参数化登录测试"""
result = login(username, password)
assert result["code"] == expected
多参数参数化
python
@pytest.mark.parametrize("user,expected_status,expected_msg", [
({"username": "admin", "password": "123456"}, 200, "成功"),
({"username": "admin", "password": "wrong"}, 401, "密码错误"}),
({"username": "", "password": "123456"}, 400, "用户名不能为空"}),
])
def test_login_complex(user, expected_status, expected_msg):
response = requests.post(url, json=user)
assert response.status_code == expected_status
assert expected_msg in response.json()["message"]
14.5 标记(Marker)
自定义标记
python
import pytest
@pytest.mark.smoke # 冒烟测试
@pytest.mark.api # API测试
@pytest.mark.login # 登录相关
def test_login():
pass
@pytest.mark.slow # 慢测试
def test_heavy_load():
pass
运行指定标记
bash
# 只运行冒烟测试
pytest -m smoke
# 排除慢测试
pytest -m "not slow"
# 运行多个标记
pytest -m "smoke and api"
配置标记(pytest.ini)
ini
[pytest]
markers =
smoke: 冒烟测试
api: API接口测试
login: 登录相关
slow: 慢测试
14.6 钩子函数(Hook)
常用钩子
python
# conftest.py
import pytest
def pytest_collection_modifyitems(config, items):
"""收集测试用例后执行"""
# 自动添加标记
for item in items:
if "login" in item.nodeid:
item.add_marker(pytest.mark.login)
def pytest_runtest_setup(item):
"""每个测试用例执行前"""
print(f"\n开始执行: {item.name}")
def pytest_runtest_teardown(item, nextitem):
"""每个测试用例执行后"""
print(f"执行结束: {item.name}")
14.7 完整的接口自动化项目
项目结构
api_test/
├── pytest.ini # pytest配置
├── conftest.py # 全局Fixture和钩子
├── requirements.txt # 依赖
├── config/
│ └── settings.py # 配置文件
├── api/
│ ├── __init__.py
│ ├── base_api.py # 基础API封装
│ ├── user_api.py # 用户模块API
│ └── order_api.py # 订单模块API
├── tests/
│ ├── __init__.py
│ ├── test_user.py # 用户测试
│ └── test_order.py # 订单测试
├── data/
│ ├── users.csv # 测试数据
│ └── orders.json
└── reports/ # 测试报告
配置文件
config/settings.py:
python
import os
class Settings:
BASE_URL = os.getenv("BASE_URL", "https://api.example.com")
TIMEOUT = int(os.getenv("TIMEOUT", "10"))
USERNAME = os.getenv("TEST_USERNAME", "admin")
PASSWORD = os.getenv("TEST_PASSWORD", "123456")
settings = Settings()
基础API封装
api/base_api.py:
python
import requests
from config.settings import settings
class BaseAPI:
def __init__(self):
self.session = requests.Session()
self.session.headers.update({
"Content-Type": "application/json",
"Accept": "application/json"
})
self.base_url = settings.BASE_URL
def request(self, method, path, **kwargs):
url = f"{self.base_url}{path}"
kwargs.setdefault("timeout", settings.TIMEOUT)
response = self.session.request(method, url, **kwargs)
return response
def get(self, path, **kwargs):
return self.request("GET", path, **kwargs)
def post(self, path, **kwargs):
return self.request("POST", path, **kwargs)
def put(self, path, **kwargs):
return self.request("PUT", path, **kwargs)
def delete(self, path, **kwargs):
return self.request("DELETE", path, **kwargs)
用户模块API
api/user_api.py:
python
from api.base_api import BaseAPI
class UserAPI(BaseAPI):
def login(self, username, password):
return self.post("/api/v1/login", json={
"username": username,
"password": password
})
def create_user(self, user_data):
return self.post("/api/v1/users", json=user_data)
def get_user(self, user_id):
return self.get(f"/api/v1/users/{user_id}")
def update_user(self, user_id, user_data):
return self.put(f"/api/v1/users/{user_id}", json=user_data)
def delete_user(self, user_id):
return self.delete(f"/api/v1/users/{user_id}")
测试用例
tests/test_user.py:
python
import pytest
from api.user_api import UserAPI
from config.settings import settings
@pytest.fixture
def user_api():
api = UserAPI()
# 登录获取token
response = api.login(settings.USERNAME, settings.PASSWORD)
token = response.json()["data"]["token"]
api.session.headers.update({"Authorization": f"Bearer {token}"})
return api
class TestUser:
def test_create_user(self, user_api):
"""测试创建用户"""
data = {
"username": "testuser001",
"email": "test001@example.com",
"password": "123456"
}
response = user_api.create_user(data)
assert response.status_code == 201
result = response.json()
assert result["code"] == 0
assert result["data"]["username"] == "testuser001"
# 保存用户ID
pytest.user_id = result["data"]["id"]
def test_get_user(self, user_api):
"""测试获取用户"""
user_id = getattr(pytest, "user_id", 1)
response = user_api.get_user(user_id)
assert response.status_code == 200
assert response.json()["data"]["id"] == user_id
@pytest.mark.parametrize("username,expected_code", [
("ab", 400), # 太短
("a" * 21, 400), # 太长
("", 400), # 为空
])
def test_create_user_invalid_username(self, user_api, username, expected_code):
"""测试无效用户名"""
data = {
"username": username,
"email": "test@example.com",
"password": "123456"
}
response = user_api.create_user(data)
assert response.status_code == expected_code
conftest.py
python
import pytest
def pytest_configure(config):
"""pytest配置"""
config.addinivalue_line("markers", "smoke: 冒烟测试")
config.addinivalue_line("markers", "api: API测试")
def pytest_collection_modifyitems(config, items):
"""自动添加标记"""
for item in items:
if "test_" in item.nodeid:
item.add_marker(pytest.mark.api)
@pytest.fixture(scope="session", autouse=True)
def setup_session():
"""会话级setup"""
print("\n=== 测试会话开始 ===")
yield
print("\n=== 测试会话结束 ===")
pytest.ini
ini
[pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts = -v --tb=short --strict-markers
markers =
smoke: 冒烟测试
api: API接口测试
slow: 慢测试
14.8 生成测试报告
HTML报告
bash
# 安装插件
pip install pytest-html
# 生成报告
pytest --html=reports/report.html --self-contained-html
Allure报告
bash
# 安装
pip install allure-pytest
# 运行测试并生成Allure数据
pytest --alluredir=reports/allure
# 生成并打开报告
allure serve reports/allure
14.9 本章小结
pytest核心特性
#mermaid-svg-fq6jSU1fDazaMQM4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fq6jSU1fDazaMQM4 .error-icon{fill:#552222;}#mermaid-svg-fq6jSU1fDazaMQM4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fq6jSU1fDazaMQM4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fq6jSU1fDazaMQM4 .marker.cross{stroke:#333333;}#mermaid-svg-fq6jSU1fDazaMQM4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fq6jSU1fDazaMQM4 p{margin:0;}#mermaid-svg-fq6jSU1fDazaMQM4 .edge{stroke-width:3;}#mermaid-svg-fq6jSU1fDazaMQM4 .section--1 rect,#mermaid-svg-fq6jSU1fDazaMQM4 .section--1 path,#mermaid-svg-fq6jSU1fDazaMQM4 .section--1 circle,#mermaid-svg-fq6jSU1fDazaMQM4 .section--1 polygon,#mermaid-svg-fq6jSU1fDazaMQM4 .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .section--1 text{fill:#ffffff;}#mermaid-svg-fq6jSU1fDazaMQM4 .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-depth--1{stroke-width:17;}#mermaid-svg-fq6jSU1fDazaMQM4 .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled circle,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:lightgray;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:#efefef;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-0 rect,#mermaid-svg-fq6jSU1fDazaMQM4 .section-0 path,#mermaid-svg-fq6jSU1fDazaMQM4 .section-0 circle,#mermaid-svg-fq6jSU1fDazaMQM4 .section-0 polygon,#mermaid-svg-fq6jSU1fDazaMQM4 .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-fq6jSU1fDazaMQM4 .section-0 text{fill:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .node-icon-0{font-size:40px;color:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-depth-0{stroke-width:14;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled circle,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:lightgray;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:#efefef;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-1 rect,#mermaid-svg-fq6jSU1fDazaMQM4 .section-1 path,#mermaid-svg-fq6jSU1fDazaMQM4 .section-1 circle,#mermaid-svg-fq6jSU1fDazaMQM4 .section-1 polygon,#mermaid-svg-fq6jSU1fDazaMQM4 .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .section-1 text{fill:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .node-icon-1{font-size:40px;color:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-depth-1{stroke-width:11;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled circle,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:lightgray;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:#efefef;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-2 rect,#mermaid-svg-fq6jSU1fDazaMQM4 .section-2 path,#mermaid-svg-fq6jSU1fDazaMQM4 .section-2 circle,#mermaid-svg-fq6jSU1fDazaMQM4 .section-2 polygon,#mermaid-svg-fq6jSU1fDazaMQM4 .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .section-2 text{fill:#ffffff;}#mermaid-svg-fq6jSU1fDazaMQM4 .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-depth-2{stroke-width:8;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled circle,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:lightgray;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:#efefef;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-3 rect,#mermaid-svg-fq6jSU1fDazaMQM4 .section-3 path,#mermaid-svg-fq6jSU1fDazaMQM4 .section-3 circle,#mermaid-svg-fq6jSU1fDazaMQM4 .section-3 polygon,#mermaid-svg-fq6jSU1fDazaMQM4 .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .section-3 text{fill:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .node-icon-3{font-size:40px;color:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-depth-3{stroke-width:5;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled circle,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:lightgray;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:#efefef;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-4 rect,#mermaid-svg-fq6jSU1fDazaMQM4 .section-4 path,#mermaid-svg-fq6jSU1fDazaMQM4 .section-4 circle,#mermaid-svg-fq6jSU1fDazaMQM4 .section-4 polygon,#mermaid-svg-fq6jSU1fDazaMQM4 .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .section-4 text{fill:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .node-icon-4{font-size:40px;color:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-depth-4{stroke-width:2;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled circle,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:lightgray;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:#efefef;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-5 rect,#mermaid-svg-fq6jSU1fDazaMQM4 .section-5 path,#mermaid-svg-fq6jSU1fDazaMQM4 .section-5 circle,#mermaid-svg-fq6jSU1fDazaMQM4 .section-5 polygon,#mermaid-svg-fq6jSU1fDazaMQM4 .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .section-5 text{fill:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .node-icon-5{font-size:40px;color:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-depth-5{stroke-width:-1;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled circle,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:lightgray;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:#efefef;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-6 rect,#mermaid-svg-fq6jSU1fDazaMQM4 .section-6 path,#mermaid-svg-fq6jSU1fDazaMQM4 .section-6 circle,#mermaid-svg-fq6jSU1fDazaMQM4 .section-6 polygon,#mermaid-svg-fq6jSU1fDazaMQM4 .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .section-6 text{fill:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .node-icon-6{font-size:40px;color:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-depth-6{stroke-width:-4;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled circle,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:lightgray;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:#efefef;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-7 rect,#mermaid-svg-fq6jSU1fDazaMQM4 .section-7 path,#mermaid-svg-fq6jSU1fDazaMQM4 .section-7 circle,#mermaid-svg-fq6jSU1fDazaMQM4 .section-7 polygon,#mermaid-svg-fq6jSU1fDazaMQM4 .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .section-7 text{fill:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .node-icon-7{font-size:40px;color:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-depth-7{stroke-width:-7;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled circle,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:lightgray;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:#efefef;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-8 rect,#mermaid-svg-fq6jSU1fDazaMQM4 .section-8 path,#mermaid-svg-fq6jSU1fDazaMQM4 .section-8 circle,#mermaid-svg-fq6jSU1fDazaMQM4 .section-8 polygon,#mermaid-svg-fq6jSU1fDazaMQM4 .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .section-8 text{fill:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .node-icon-8{font-size:40px;color:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-depth-8{stroke-width:-10;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled circle,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:lightgray;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:#efefef;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-9 rect,#mermaid-svg-fq6jSU1fDazaMQM4 .section-9 path,#mermaid-svg-fq6jSU1fDazaMQM4 .section-9 circle,#mermaid-svg-fq6jSU1fDazaMQM4 .section-9 polygon,#mermaid-svg-fq6jSU1fDazaMQM4 .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .section-9 text{fill:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .node-icon-9{font-size:40px;color:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-depth-9{stroke-width:-13;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled circle,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:lightgray;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:#efefef;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-10 rect,#mermaid-svg-fq6jSU1fDazaMQM4 .section-10 path,#mermaid-svg-fq6jSU1fDazaMQM4 .section-10 circle,#mermaid-svg-fq6jSU1fDazaMQM4 .section-10 polygon,#mermaid-svg-fq6jSU1fDazaMQM4 .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .section-10 text{fill:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .node-icon-10{font-size:40px;color:black;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .edge-depth-10{stroke-width:-16;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled circle,#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:lightgray;}#mermaid-svg-fq6jSU1fDazaMQM4 .disabled text{fill:#efefef;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-root rect,#mermaid-svg-fq6jSU1fDazaMQM4 .section-root path,#mermaid-svg-fq6jSU1fDazaMQM4 .section-root circle,#mermaid-svg-fq6jSU1fDazaMQM4 .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-fq6jSU1fDazaMQM4 .section-root text{fill:#ffffff;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-root span{color:#ffffff;}#mermaid-svg-fq6jSU1fDazaMQM4 .section-2 span{color:#ffffff;}#mermaid-svg-fq6jSU1fDazaMQM4 .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-fq6jSU1fDazaMQM4 .edge{fill:none;}#mermaid-svg-fq6jSU1fDazaMQM4 .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-fq6jSU1fDazaMQM4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} pytest
基础
assert断言
自动发现
详细报告
Fixture
作用域控制
setup/teardown
依赖注入
参数化
多组数据
多参数
自动展开
标记
自定义marker
选择性执行
分类管理
插件
HTML报告
Allure报告
覆盖率
课后练习 📝
-
基础题:用pytest编写一个计算器测试,包含加、减、乘、除。
-
进阶题:按照上面的项目结构,搭建一个完整的接口自动化项目。
14.10 下章预告
下一章我们将学习Java + RestAssured进行接口自动化!
"pytest让Python测试变得简单而优雅,它是接口自动化测试的最佳搭档。"