✅ 先给核心结论(一句话戳破本质)
这两段「创建订单」的代码,完全不是重复!而是「职责天差地别」的两段代码 —— 哪怕代码写法看着一样,它们的目的、作用、归属完全不同,缺一不可。新手最容易犯的错误就是把「测试用例的核心动作」和「测试用例的前置依赖」搞混,误以为代码写法相似就是重复。
✅ 生活化类比(彻底理解抽象区别,重中之重)
用「餐厅运营」的场景,完美对应你的测试场景,瞬间看懂两者差异:
🔹 场景1:conftest.py 中「创建订单」→ 【后厨提前备好的通用食材】
目的:给其他需要订单ID的用例(比如「删除订单」)提供前置依赖数据。
类比:餐厅后厨会提前备好「米饭」这种通用食材,不管顾客点什么菜(鱼香肉丝、宫保鸡丁),只要需要配米饭,后厨都能直接提供。这里的「米饭」= conftest.py 创建的「订单ID」,「需要米饭的菜品」= test/delete_order.py(需要订单ID才能执行删除)。
核心:它不是「要测试的内容」,只是帮其他测试用例跑起来的「垫脚石」。
🔹 场景2:test/add_order.py 中「创建订单」→ 【顾客完整的点单用餐流程】
目的:验证「新增订单」这个接口本身是否有效,是测试用例的核心动作、唯一目标。
类比:顾客进店专门点了一份「招牌炒饭」,服务员下单、后厨制作、顾客用餐,整个流程的核心就是「确认炒饭好不好吃、能不能正常上桌」。这里的「点炒饭」= test/add_order.py 调用创建订单接口,「确认炒饭品质」= 断言订单创建成功。
核心:它就是测试本身,是你要验证的功能,不是垫脚石。
✅ 两者3大核心差异(表格更清晰)
✅ 完整代码示例(贴合你的文件结构,可直接套用)
严格对应你的目录:conftest.py、test/add_order.py、test/delete_order.py,基于pytest实现,完美还原业务场景,注释清晰易懂。
目录结构
项目根目录/
├── conftest.py # 公共前置依赖(提供订单ID)
└── test/
├── add_order.py # 新增订单用例(测试核心功能)
└── delete_order.py# 删除订单用例(依赖订单ID)1. conftest.py → 提供删除订单所需的「订单ID」(依赖数据)
import pytest
import requests
# 定义pytest夹具,作用:生成订单ID,供所有需要的用例调用
# scope="function":每个用例执行前都生成一个全新的订单ID,避免数据污染
@pytest.fixture(scope="function")
def get_order_id():
"""前置依赖:创建一条临时订单,返回订单ID,给删除/查询用例使用"""
# 调用新增订单接口,生成数据(这是「垫脚石」代码)
url = "http://xxx-api.com/order/add"
data = {"goods_name": "测试商品", "price": 99, "num": 1}
res = requests.post(url, json=data)
order_id = res.json()["data"]["order_id"]
yield order_id # 返回订单ID,供下游用例使用
# 【可选】用例执行完后,自动清理数据(后置操作,更规范)
requests.post("http://xxx-api.com/order/delete", json={"order_id": order_id})2. test/add_order.py → 测试「新增订单」核心功能(独立创建订单)
import requests
def test_add_order_success():
"""测试用例:验证新增订单接口功能正常(核心动作)"""
# 调用新增订单接口(这是「测试本身」的代码)
url = "http://xxx-api.com/order/add"
data = {"goods_name": "Python测试商品", "price": 199, "num": 2}
res = requests.post(url, json=data)
# ✅ 核心:必须断言,验证新增功能是否达标(这是该用例的唯一意义)
assert res.status_code == 200 # 接口请求成功
assert res.json()["code"] == 0 # 业务逻辑成功
assert "order_id" in res.json()["data"] # 返回正确的订单ID
print("新增订单用例执行成功,订单ID:", res.json()["data"]["order_id"])3. test/delete_order.py → 测试「删除订单」功能(复用conftest的订单ID)
import requests
def test_delete_order_success(get_order_id):
"""测试用例:验证删除订单接口功能正常
入参get_order_id:直接调用conftest中的夹具,拿到订单ID
"""
url = "http://xxx-api.com/order/delete"
data = {"order_id": get_order_id} # 复用夹具生成的订单ID
res = requests.post(url, json=data)
# ✅ 断言删除功能是否达标
assert res.status_code == 200
assert res.json()["code"] == 0
assert res.json()["msg"] == "删除成功"
print(f"删除订单用例执行成功,删除的订单ID:{get_order_id}")✅ 你真正关心的:如何解决「代码写法相似」的问题(真正去重)
你担心的「代码重复」,本质是 「两段代码里都写了「调用新增订单接口」的逻辑」 —— 这部分代码确实可以合并!我们的原则是:
✅ 保留「职责差异」,合并「重复的业务逻辑代码」
解决方案:抽离「公共函数」(最优实践,企业级标准)
新增一个工具文件,把「创建订单」的核心逻辑封装成独立函数,让 conftest.py 和 test/add_order.py 都调用这个函数,既消除代码重复,又不改变两者的职责。
步骤1:新增工具文件 utils/order_utils.py → 封装公共逻辑
import requests
def create_order(goods_name="默认测试商品", price=99, num=1):
"""公共函数:调用新增订单接口,返回订单ID
可自定义商品名称、价格、数量,适配不同场景
"""
url = "http://xxx-api.com/order/add"
data = {"goods_name": goods_name, "price": price, "num": num}
res = requests.post(url, json=data)
return res.json()["data"]["order_id"]步骤2:改造 conftest.py → 调用公共函数
import pytest
from utils.order_utils import create_order # 导入公共函数
@pytest.fixture(scope="function")
def get_order_id():
order_id = create_order() # 调用公共函数,生成订单ID
yield order_id
# 后置清理
requests.post("http://xxx-api.com/order/delete", json={"order_id": order_id})步骤3:改造 test/add_order.py → 调用公共函数
import requests
from utils.order_utils import create_order # 导入公共函数
def test_add_order_success():
# 调用公共函数,创建订单
order_id = create_order(goods_name="Python测试商品", price=199, num=2)
# 断言(验证创建结果)
assert order_id is not None and len(order_id) > 0
print("新增订单用例执行成功,订单ID:", order_id)✅ 最终效果:两段代码的职责完全不变,但「创建订单」的重复逻辑被彻底抽离,后续修改接口地址/入参时,只需要改 order_utils.py 一个文件即可,完美解决你的顾虑!
✅ 新手3个高频误解点(避坑指南,必看)
❌ 误区1:「代码写法相似」=「代码重复」
✔️ 纠正:判断是否重复,不看代码写法,看代码的「职责和目的」。
比如:厨师「备米饭」和顾客「点米饭」,动作都是「盛米饭」,但目的完全不同,不能说重复。同理,conftest创建订单是「备数据」,add_order创建订单是「测功能」,写法相似但职责不同,不是重复。
❌ 误区2:为了去重,让 test/add_order.py 复用 conftest 的订单ID
✔️ 纠正:绝对禁止!会导致用例污染、功能验证失效。
如果add_order用例直接用conftest的订单ID,相当于「没有真正调用新增接口」,只是拿了别人创建的ID,这个用例就失去了「测试新增订单功能」的意义,等于白测。
❌ 误区3:fixture作用域设置不当,导致数据冲突
✔️ 纠正:给 conftest 的夹具设置 scope="function"(默认),每个用例都生成全新的订单ID。
如果设置成 scope="session"(全局),所有删除用例会共用同一个订单ID,第一个用例删除后,后续用例会因为「订单不存在」执行失败,这是新手最容易踩的坑。
✅ 最终总结(提炼核心,一眼记住)
核心区别:
conftest创建订单 → 给别人用(依赖数据);add_order创建订单 → 自己测(核心功能),二者不是重复;去重方法:抽离「创建订单」为公共函数,二者均调用该函数,保留职责、合并逻辑;
核心原则:测试用例的「前置依赖」和「核心动作」必须分离,不能混为一谈。
这套方案既解决了你担心的「代码重复」问题,又完全符合接口自动化的工程化规范,可直接在项目中落地~