未命名文章

作者:c_chun 发布时间: 2025-12-31 阅读量:4 评论数:0

✅ 先给核心结论(一句话戳破本质)

这两段「创建订单」的代码,完全不是重复!而是「职责天差地别」的两段代码 —— 哪怕代码写法看着一样,它们的目的、作用、归属完全不同,缺一不可。新手最容易犯的错误就是把「测试用例的核心动作」和「测试用例的前置依赖」搞混,误以为代码写法相似就是重复。

✅ 生活化类比(彻底理解抽象区别,重中之重)

用「餐厅运营」的场景,完美对应你的测试场景,瞬间看懂两者差异:

🔹 场景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 中创建订单

核心目的

生成「订单ID」,给删除/查询订单用例提供依赖数据

验证「新增订单接口」的功能是否正常(成功创建、返回正确字段等)

归属角色

测试的「前置条件/辅助工具」

测试的「核心执行动作/验证目标」

执行逻辑

隐式执行(被pytest自动调用),用户不关心创建结果,只拿ID

显式执行(用例本身),必须断言创建结果(成功/失败),验证功能

✅ 完整代码示例(贴合你的文件结构,可直接套用)

严格对应你的目录:conftest.pytest/add_order.pytest/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.pytest/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,第一个用例删除后,后续用例会因为「订单不存在」执行失败,这是新手最容易踩的坑。

✅ 最终总结(提炼核心,一眼记住)

  1. 核心区别conftest 创建订单 → 给别人用(依赖数据);add_order 创建订单 → 自己测(核心功能),二者不是重复;

  2. 去重方法:抽离「创建订单」为公共函数,二者均调用该函数,保留职责、合并逻辑;

  3. 核心原则:测试用例的「前置依赖」和「核心动作」必须分离,不能混为一谈。

这套方案既解决了你担心的「代码重复」问题,又完全符合接口自动化的工程化规范,可直接在项目中落地~

评论