接口自动化测试实战:如何围绕真实业务场景设计高质量用例

前言

很多接口自动化项目写到后面,都会遇到一个问题:

接口都测了,但总觉得离真实业务还差一层。

这其实很正常,因为"接口成功"不等于"业务场景成立"。

例如,一个列表接口返回 200,只能说明请求成功,不代表:

  • 查询条件生效了

  • 返回数据真的符合预期

  • 不同用户看到的数据一致性正确

  • 前后接口联动没有问题

  • 关键数据已经真实落到了系统里

所以,接口自动化做到一定阶段以后,重点就不该只是"单接口验证",而要开始向"真实业务场景"靠拢。

我结合项目里的 testcases/业务系统A/test_api_kc_goods.pytestcases/业务系统A/test_e2e_goods_flow.pycommon/variable_store.pyconftest.py 来讲讲:怎么把接口自动化从"能跑"写到"像业务"。

一、单接口测试和业务场景测试的区别

单接口测试关注什么

  • 请求参数是否正确

  • 返回码是否正确

  • 响应结构是否正确

  • 关键字段是否存在

业务场景测试关注什么

  • 多个条件组合后结果是否正确

  • 数据是否真的和业务状态一致

  • 不同角色看到的内容是否不同

  • 先查再存、先存再查的链路是否闭合

这两者并不冲突,但优先级不同。

单接口测试更像"打底",业务场景测试才是真正验证系统可用性的地方。

二、为什么商品列表是很好的场景切入口

这个项目里,商品库存相关接口很适合做场景化展示,因为它天然有几个测试价值:

  • 列表查询本身就有分页

  • 查询条件很多,适合做组合验证

  • 同一批数据可能有不同来源、类目、上下架状态

  • 可以和数据库做一致性对比

  • 可以做普通用户与操作员的差异化验证

所以在 testcases/业务系统A/test_api_kc_goods.py 里写的那些场景,其实很有代表性。

它不是为了"凑测试数量",而是很符合企业系统中库存列表类接口的典型验证方式。

三、为什么"默认查询"是场景入口

很多复杂场景的第一步,其实就是先确认默认数据是什么样。

比如:

response = 业务系统A_inventory_service.list_inventory()

这一步的意义不是"随便查一遍",而是先拿到一个稳定的基线:

  • 当前环境中是否有数据

  • 列表结构是否正常

  • 响应外壳是否稳定

  • 后续提取字段是否可用

一旦基线建立起来,后面的筛选、组合条件、角色对比才有意义。

四、模糊搜索和精确搜索为什么都要测

在真实业务里,列表查询通常至少有两种高频方式:

1)模糊搜索

按名称搜索,适合用户不记得完整编码时使用。

2)精确搜索

按商品编码、ID 等字段搜索,适合定位某条确定数据。

项目里的测试就体现了这一点:

python 复制代码
resp_all = 业务系统A_inventory_service.list_inventory()
target_name = extract(resp_all, "data.list[0].goodsName")
target_code = extract(resp_all, "data.list[0].goodsCode")

先从默认列表里提取一个实际存在的数据,再用它回去做反向查询。

这个写法的好处是:

  1. 不依赖外部硬编码测试数据

  2. 用例更贴近真实业务

  3. 只要环境里有数据,就能跑

这类"先取数,再回查"的思路非常适合业务型接口测试。

五、为什么参数化特别适合筛选类接口

对于来源、上下架状态、分类等筛选条件,参数化是非常合适的。

因为同样是"列表筛选",测试逻辑是一样的,变化的只是输入数据和预期结果。

例如:

python 复制代码
@pytest.mark.parametrize(
    "case",
    ConfigManager().get_test_data("kc_goods.page_query.source_cases"),
    ids=lambda x: x["desc"]
)
def test_kc_goods_filter_by_source_ddt(self, 业务系统A_inventory_service, case):
    ...

这种写法有三个好处:

1)输入数据集中管理

测试数据放在 testdata/ 里,代码和数据分离。

2)新增场景更容易

要扩展一个来源值,只要往数据文件里加一条,不需要改测试逻辑。

3)报告更清晰

通过 ids=lambda x: x["desc"],测试报告里能直接看到场景描述。

这比把所有情况写成多个重复用例更适合长期维护。

六、动态数据为什么比写死数据更有价值

这个项目里一个很重要的特点,就是很多场景并不依赖写死的商品编码,而是先从系统返回值里提取,再用于后续查询。

例如:

python 复制代码
tag_resp = 业务系统A_inventory_service.list_goods_tags()
tag_list = extract(tag_resp, "data")
first_group = tag_list[0]
children = first_group.get("children", [])
target_ids = [str(item["tagId"]) for item in children[:2]]

再用提取出来的动态类目 ID 去查询商品列表:

response = 业务系统A_inventory_service.list_inventory( overrides={"categoryIdList": target_ids} )

这类设计的核心价值在于:

  • 不依赖手工维护的大量测试数据

  • 更接近真实系统运行状态

  • 能在不同环境中更稳定地复用

  • 避免因为环境数据变化导致大量硬编码失效

对于企业内部项目来说,这种"动态取数 + 反向验证"的思路很重要。

七、多角色场景为什么很值得测

很多业务系统里,权限不是附属能力,而是核心业务逻辑的一部分。

所以在项目里设计了两套客户端:

  • 业务系统A_client

  • 业务系统A_operator_client

然后在测试里对比两种角色的查询结果:

python 复制代码
admin_resp = 业务系统A_inventory_service.list_inventory()
op_resp = 业务系统A_inventory_operator_service.list_inventory()

这类用例的意义不是单纯看两个响应码,而是验证:

  • 不同角色是否能访问相同资源

  • 数据可见性是否符合权限预期

  • 当前登录身份是否真的生效

如果一个系统有角色权限模型,那这种测试是非常必要的。

八、并发查询类用例为什么值得保留

并发测试不一定非要追求性能指标,它也可以用来验证稳定性和一致性。

项目里写了一个轻量并发查询的用例:

  • 使用 ThreadPoolExecutor

  • 同时发起多个查询

  • 每个线程使用不同参数

  • 最后汇总结果并做断言

python 复制代码
@allure.story("轻量级并发-商品列表查询")
@pytest.mark.P2
def test_kc_goods_concurrent_query(self, 业务系统A_inventory_service):
    """
    模拟 5 个用户同时查询商品列表。
    注意:这里使用的是线程池,适合 I/O 密集型(接口请求)场景。
    """
    num_threads = 3
    results = []

    # 定义一个内部函数,作为线程执行的任务
    def task(thread_id):
        try:
            resp = 业务系统A_inventory_service.list_inventory(
                page_num=1,
                page_size=5,
                overrides={"goodsName": f"测试商品_{thread_id}"}  # 模拟不同参数
            )
            return {
                "thread_id": thread_id,
                "code": resp.get("code"),
                "total": extract(resp, "data.total"),
                "success": True
            }
        except Exception as e:
            return {"thread_id": thread_id, "success": False, "error": str(e)}

    # ✅ 启动线程池,最大并发数为 5
    with ThreadPoolExecutor(max_workers=num_threads) as executor:
        # 提交 5 个任务
        futures = {executor.submit(task, i): i for i in range(num_threads)}

        # 收集结果
        for future in as_completed(futures):
            result = future.result()
            results.append(result)

            # 可以在这里对每个并发的结果进行即时断言
            assert result["success"], f"线程 {result['thread_id']} 执行失败: {result.get('error')}"
            Assertions.assert_eq(result, "code", "200")

    # ✅ 最终断言:确保 5 个请求都成功了
    assert len(results) == num_threads, "并发任务数量不符"

    # 可以在 Allure 报告里记录并发结果
    allure.attach(
        str(results),
        name="Concurrent Results Summary",
        attachment_type=allure.attachment_type.JSON
    )

这个设计的价值有几个:

1)验证接口基本线程安全

至少能看出接口在并发访问时是否有明显异常。

2)验证测试框架本身是否能支持并发执行

有些问题不是业务接口的问题,而是测试代码共享变量或 session 使用不当。

3)帮助发现偶发性问题

某些接口在单线程下没事,但并发下会暴露边界问题。

对于业务自动化来说,少量轻量并发场景非常值得保留。

九、为什么要做查询结果为空的负向场景

好的接口自动化,不应该只测成功路径。

失败路径同样重要,比如:

  • 查不到数据时怎么返回

  • 参数不合法时怎么返回

  • 空列表场景怎么处理

test_kc_goods_query_not_found 里就体现了这种思路:

python 复制代码
response = 业务系统A_inventory_service.list_inventory(
    overrides={"categoryIdList": ["随便乱写"]},
    assert_response=False
)
Assertions.assert_eq(response, "code", "400")
Assertions.assert_eq(response, "successful", False)
Assertions.assert_str(response, "message", "数据参数不正确")

这个写法很实用,因为它把"异常响应应该长什么样"也纳入了测试范围。

对业务系统来说,错误信息是否稳定、是否可理解、是否能被前端正确处理,都是测试关注点。

十、为什么 E2E 场景一定要做上下文传递

端到端场景最关键的,不是接口多,而是数据怎么传。

比如在 test_e2e_goods_flow.py 里用了几种方式保存中间变量:

单个用例内:局部变量 > scene_context 多个用例/会话级:fixture > shared_context

1)scene_context/局部变量

适合同一个场景内临时传递。

python 复制代码
 scene_context 不是为了让单行代码更短,而是为了让"复杂业务流程"里每个步骤之间共享同一批中间数据。 
 即scene_context 只有在单个用例内部流程比较复杂时才考虑使用                                            
                                                                                                                                                    
  你可以把它理解成:当前这个 E2E 用例专属的"临时数据袋"。                                                                                           
                                                                                                                                                    
  ---------                                                                                                                                               
                                                                                                                                                    
  假设我们要测一个完整流程:                                                                                                                        
                                                                                                                                                    
  1. 创建商品
  2. 给商品创建库存                                                                                                                                 
  3. 基于这个库存商品创建营销活动                                                                                                                   
  4. 校验营销活动和商品绑定正确                                                                                                                     
  5. 清理测试数据                                                                                                                                   
                                                                                                                                                    
  这个流程里,每一步会产生一些后面步骤要用的数据:                                                                                                  
                                                                                                                                                    
  创建商品 -> 得到 goods_id、goods_code                                                                                                             
  创建库存 -> 需要 goods_id,同时产生 inventory_id                                                                                                  
  创建营销活动 -> 需要 goods_id、goods_code、inventory_id,同时产生 activity_id                                                                     
  校验营销活动 -> 需要 activity_id、goods_code                                                                                                      
  清理数据 -> 需要 activity_id、inventory_id、goods_id                                                                                              
                                                                                                                                                    
  如果不用 scene_context,你需要不断接收返回值、继续传参。                                                                                          
                                                                                                                                                    
  ---------                                                                                                                                               
                                                                                                                                                    
  不用 scene_context 的写法                                                                                                                         
                                                                                                                                                    
  大概会变成这样:                                                                                                                                  
                                                                                                                                                    
  def test_goods_full_flow(self, 业务系统A_inventory_service):                                                                                           
      goods_id, goods_code = self._create_goods(业务系统A_inventory_service)                                                                             
                                                                                                                                                    
      inventory_id = self._create_inventory(                                                                                                        
          业务系统A_inventory_service,                                                                                                                   
          goods_id,                                                                                                                                 
          goods_code,                                                                                                                               
      )                                                                                                                                             
                                                                                                                                                    
      activity_id = self._create_marketing(                                                                                                         
          业务系统A_inventory_service,                                                                                                                   
          goods_id,                                                                                                                                 
          goods_code,                                                                                                                               
          inventory_id,                                                                                                                             
      )                                                                                                                                             
                                                                                                                                                    
      self._assert_marketing(                                                                                                                       
          业务系统A_inventory_service,                                                                                                                   
          activity_id,                                                                                                                              
          goods_code,                                                                                                                               
      )                                                                                                                                             
                                                                                                                                                    
      self._cleanup(                                                                                                                                
          业务系统A_inventory_service,                                                                                                                   
          activity_id,                                                                                                                              
          inventory_id,                                                                                                                             
          goods_id,                                                                                                                                 
      )                                                                                                                                             
                                                                                                                                                    
  这个例子还不算复杂,但你已经能看到参数开始变多了。                                                                                                
                                                                                                                                                    
  每个步骤函数可能长这样:                                                                                                                          
                                                                                                                                                    
  def _create_goods(self, service):                                                                                                                 
      resp = service.create_goods(...)                                                                                                              
      goods_id = extract(resp, "data.goodsId")                                                                                                      
      goods_code = extract(resp, "data.goodsCode")                                                                                                  
                                                                                                                                                    
      assert goods_id, "goods_id 不应为空"                                                                                                          
      assert goods_code, "goods_code 不应为空"                                                                                                      
                                                                                                                                                    
      return goods_id, goods_code                                                                                                                   
                                                                                                                                                    
  def _create_inventory(self, service, goods_id, goods_code):                                                                                       
      resp = service.create_inventory(                                                                                                              
          goods_id=goods_id,                                                                                                                        
          goods_code=goods_code,                                                                                                                    
      )                                                                                                                                             
                                                                                                                                                    
      inventory_id = extract(resp, "data.inventoryId")                                                                                              
      assert inventory_id, "inventory_id 不应为空"                                                                                                  
                                                                                                                                                    
      return inventory_id                                                                                                                           
                                                                                                                                                    
  def _create_marketing(self, service, goods_id, goods_code, inventory_id):                                                                         
      resp = service.create_marketing(                                                                                                              
          goods_id=goods_id,                                                                                                                        
          goods_code=goods_code,                                                                                                                    
          inventory_id=inventory_id,                                                                                                                
      )                                                                                                                                             
                                                                                                                                                    
      activity_id = extract(resp, "data.activityId")                                                                                                
      assert activity_id, "activity_id 不应为空"                                                                                                    
                                                                                                                                                    
      return activity_id                                                                                                                            
                                                                                                                                                    
  def _assert_marketing(self, service, activity_id, goods_code):                                                                                    
      resp = service.query_marketing(activity_id=activity_id)                                                                                       
                                                                                                                                                    
      Assertions.assert_eq(resp, "data.activityId", activity_id)                                                                                    
      Assertions.assert_eq(resp, "data.goodsCode", goods_code)                                                                                      
                                                                                                                                                    
  def _cleanup(self, service, activity_id, inventory_id, goods_id):                                                                                 
      service.delete_marketing(activity_id=activity_id)                                                                                             
      service.delete_inventory(inventory_id=inventory_id)                                                                                           
      service.delete_goods(goods_id=goods_id)                                                                                                       
                                                                                                                                                    
  这种写法是可以的,而且在流程不复杂时非常推荐。                                                                                                    
                                                                                                                                                    
  但问题是,如果流程继续变复杂:                                                                                                                    
                                                                                                                                                    
  goods_id                                                                                                                                          
  goods_code                                                                                                                                        
  sku_id                                                                                                                                            
  warehouse_id                                                                                                                                      
  inventory_id                                                                                                                                      
  activity_id                                                                                                                                       
  activity_code                                                                                                                                     
  tenant_id                                                                                                                                         
  operator_id                                                                                                                                       
  price_id                                                                                                                                          
  stock_batch_id                                                                                                                                    
                                                                                                                                                    
  那么你的函数调用可能变成这样:                                                                                                                    
                                                                                                                                                    
  activity_id = self._create_marketing(                                                                                                             
      业务系统A_inventory_service,                                                                                                                       
      goods_id,                                                                                                                                     
      goods_code,                                                                                                                                   
      sku_id,                                                                                                                                       
      warehouse_id,                                                                                                                                 
      inventory_id,                                                                                                                                 
      tenant_id,                                                                                                                                    
      operator_id,                                                                                                                                  
      price_id,                                                                                                                                     
  )                                                                                                                                                 
                                                                                                                                                    
  清理函数也可能变成:                                                                                                                              
                                                                                                                                                    
  self._cleanup(                                                                                                                                    
      业务系统A_inventory_service,                                                                                                                       
      activity_id,                                                                                                                                  
      activity_code,                                                                                                                                
      inventory_id,                                                                                                                                 
      stock_batch_id,                                                                                                                               
      price_id,                                                                                                                                     
      goods_id,                                                                                                                                     
      sku_id,                                                                                                                                       
      warehouse_id,                                                                                                                                 
  )                                                                                                                                                 
                                                                                                                                                    
  这时候维护成本就会变高。                                                                                                                          
                                                                                                                                                    
  ---------                                                                                                                                               
                                                                                                                                                    
  使用 scene_context 的写法                                                                                                                         

  有了 scene_context 后,主流程可以变成:                                                                                                           
                                                                                                                                                    
  def test_goods_full_flow(self, 业务系统A_inventory_service, scene_context):                                                                            
      self._create_goods(业务系统A_inventory_service, scene_context)                                                                                     
      self._create_inventory(业务系统A_inventory_service, scene_context)                                                                                 
      self._create_marketing(业务系统A_inventory_service, scene_context)                                                                                 
      self._assert_marketing(业务系统A_inventory_service, scene_context)                                                                                 
      self._cleanup(业务系统A_inventory_service, scene_context)                                                                                          
                                                                                                                                                    
  这段代码像一个业务流程说明:                                                                                                                      
                                                                                                                                                    
  创建商品 -> 创建库存 -> 创建营销活动 -> 校验营销活动 -> 清理                                                                                      
                                                                                                                                                    
  它不关心每一步具体产生了哪些变量,只负责安排步骤顺序。                                                                                            
                                                                                                                                                    
  ---------                                                                                                                                               
                                                                                                                                                    
  第一步:创建商品                                                                                                                                  
                                                                                                                                                    
  def _create_goods(self, service, ctx):                                                                                                            
      resp = service.create_goods(...)                                                                                                              
                                                                                                                                                    
      goods_id = extract(resp, "data.goodsId")                                                                                                      
      goods_code = extract(resp, "data.goodsCode")                                                                                                  
                                                                                                                                                    
      assert goods_id, "goods_id 不应为空"                                                                                                          
      assert goods_code, "goods_code 不应为空"                                                                                                      
                                                                                                                                                    
      ctx["goods_id"] = goods_id                                                                                                                    
      ctx["goods_code"] = goods_code                                                                                                                
                                                                                                                                                    
  这一步做了三件事:                                                                                                                                
                                                                                                                                                    
  1. 调接口创建商品                                                                                                                                 
  2. 从响应里提取 goods_id、goods_code                                                                                                              
  3. 放进 scene_context                                                                                                                             
                                                                                                                                                    
  执行完后,scene_context 里面大概是:                                                                                                              
                                                                                                                                                    
  {                                                                                                                                                 
      "goods_id": "10001",                                                                                                                          
      "goods_code": "G202609010001"                                                                                                                 
  }                                                                                                                                                 
                                                                                                                                                    
  注意:这里确实还是定义了局部变量 goods_id、goods_code。                                                                                           
                                                                                                                                                    
  所以你前面说得对,scene_context 没有减少这一步的变量定义。                                                                                        
                                                                                                                                                    
  它真正的作用是:让后面的步骤不用通过返回值拿这些数据,也不用在主函数里继续传下去。                                                                
                                                                                                                                                    
  ---------                                                                                                                                               
                                                                                                                                                    
  第二步:创建库存                                                                                                                                  
                                                                                                                                                    
  def _create_inventory(self, service, ctx):                                                                                                        
      resp = service.create_inventory(                                                                                                              
          goods_id=ctx["goods_id"],                                                                                                                 
          goods_code=ctx["goods_code"],                                                                                                             
      )                                                                                                                                             
                                                                                                                                                    
      inventory_id = extract(resp, "data.inventoryId")                                                                                              
      assert inventory_id, "inventory_id 不应为空"                                                                                                  
                                                                                                                                                    
      ctx["inventory_id"] = inventory_id                                                                                                            
                                                                                                                                                    
  这里用到了第一步放进去的数据:                                                                                                                    
                                                                                                                                                    
  ctx["goods_id"]                                                                                                                                   
  ctx["goods_code"]                                                                                                                                 
                                                                                                                                                    
  然后它又产生了新的数据:                                                                                                                          
                                                                                                                                                    
  ctx["inventory_id"] = inventory_id                                                                                                                
                                                                                                                                                    
  执行完后,scene_context 变成:                                                                                                                    
                                                                                                                                                    
  {                                                                                                                                                 
      "goods_id": "10001",                                                                                                                          
      "goods_code": "G202609010001",                                                                                                                
      "inventory_id": "INV90001"                                                                                                                    
  }                                                                                                                                                 
                                                                                                                                                    
  ---------                                                                                                                                               
                                                                                                                                                    
  第三步:创建营销活动                                                                                                                              
                                                                                                                                                    
  def _create_marketing(self, service, ctx):                                                                                                        
      resp = service.create_marketing(                                                                                                              
          goods_id=ctx["goods_id"],                                                                                                                 
          goods_code=ctx["goods_code"],                                                                                                             
          inventory_id=ctx["inventory_id"],                                                                                                         
      )                                                                                                                                             
                                                                                                                                                    
      activity_id = extract(resp, "data.activityId")                                                                                                
      assert activity_id, "activity_id 不应为空"                                                                                                    
                                                                                                                                                    
      ctx["activity_id"] = activity_id                                                                                                              
                                                                                                                                                    
  这一步用到了前两步的数据:                                                                                                                        
                                                                                                                                                    
  ctx["goods_id"]                                                                                                                                   
  ctx["goods_code"]                                                                                                                                 
  ctx["inventory_id"]                                                                                                                               
                                                                                                                                                    
  然后产生了:                                                                                                                                      
                                                                                                                                                    
  ctx["activity_id"]                                                                                                                                
                                                                                                                                                    
  执行完后,scene_context 变成:                                                                                                                    
                                                                                                                                                    
  {                                                                                                                                                 
      "goods_id": "10001",                                                                                                                          
      "goods_code": "G202609010001",                                                                                                                
      "inventory_id": "INV90001",                                                                                                                   
      "activity_id": "ACT80001"                                                                                                                     
  }                                                                                                                                                 
                                                                                                                                                    
  ---------                                                                                                                                               
                                                                                                                                                    
  第四步:校验营销活动                                                                                                                              
                                                                                                                                                    
  def _assert_marketing(self, service, ctx):                                                                                                        
      resp = service.query_marketing(                                                                                                               
          activity_id=ctx["activity_id"],                                                                                                           
      )                                                                                                                                             
                                                                                                                                                    
      Assertions.assert_eq(resp, "data.activityId", ctx["activity_id"])                                                                             
      Assertions.assert_eq(resp, "data.goodsCode", ctx["goods_code"])                                                                               
      Assertions.assert_eq(resp, "data.inventoryId", ctx["inventory_id"])                                                                           
                                                                                                                                                    
  这一步不产生新数据,主要是消费前面产生的数据:                                                                                                    
                                                                                                                                                    
  ctx["activity_id"]                                                                                                                                
  ctx["goods_code"]                                                                                                                                 
  ctx["inventory_id"]                                                                                                                               
                                                                                                                                                    
  它验证:                                                                                                                                          
                                                                                                                                                    
  查询到的营销活动 ID 是否正确                                                                                                                      
  营销活动绑定的商品编码是否正确                                                                                                                    
  营销活动关联的库存 ID 是否正确                                                                                                                    
                                                                                                                                                    
  ---------                                                                                                                                               
                                                                                                                                                    
  第五步:清理数据                                                                                                                                  
                                                                                                                                                    
  def _cleanup(self, service, ctx):                                                                                                                 
      if ctx.get("activity_id"):                                                                                                                    
          service.delete_marketing(activity_id=ctx["activity_id"])                                                                                  
                                                                                                                                                    
      if ctx.get("inventory_id"):                                                                                                                   
          service.delete_inventory(inventory_id=ctx["inventory_id"])                                                                                
                                                                                                                                                    
      if ctx.get("goods_id"):                                                                                                                       
          service.delete_goods(goods_id=ctx["goods_id"])                                                                                            
                                                                                                                                                    
  清理函数的特点是:它可能需要前面很多步骤产生的数据。                                                                                              
                                                                                                                                                    
  如果不用 scene_context,你可能要传很多参数:                                                                                                      
                                                                                                                                                    
  self._cleanup(                                                                                                                                    
      service,                                                                                                                                      
      activity_id,                                                                                                                                  
      inventory_id,                                                                                                                                 
      goods_id,                                                                                                                                     
      sku_id,                                                                                                                                       
      price_id,                                                                                                                                     
      stock_batch_id,                                                                                                                               
  )                                                                                                                                                 
                                                                                                                                                    
  如果用 scene_context,清理函数只拿 ctx,自己判断里面有什么就清什么。                                                                              
                                                                                                                                                    
  这就是 scene_context 比较适合的地方。                                                                                                             
                                                                                                                                                    
  ---------                                                                                                                                               
                                                                                                                                                    
  完整版本                                                                                                                                          
                                                                                                                                                    
  把上面的合起来就是:                                                                                                                              
                                                                                                                                                    
  def test_goods_full_flow(self, 业务系统A_inventory_service, scene_context):                                                                            
      self._create_goods(业务系统A_inventory_service, scene_context)                                                                                     
      self._create_inventory(业务系统A_inventory_service, scene_context)                                                                                 
      self._create_marketing(业务系统A_inventory_service, scene_context)                                                                                 
      self._assert_marketing(业务系统A_inventory_service, scene_context)                                                                                 
      self._cleanup(业务系统A_inventory_service, scene_context)                                                                                          
                                                                                                                                                    
                                                                                                                                                    
  def _create_goods(self, service, ctx):                                                                                                            
      resp = service.create_goods(...)                                                                                                              
                                                                                                                                                    
      goods_id = extract(resp, "data.goodsId")                                                                                                      
      goods_code = extract(resp, "data.goodsCode")                                                                                                  
                                                                                                                                                    
      assert goods_id, "goods_id 不应为空"                                                                                                          
      assert goods_code, "goods_code 不应为空"                                                                                                      
                                                                                                                                                    
      ctx["goods_id"] = goods_id                                                                                                                    
      ctx["goods_code"] = goods_code                                                                                                                
                                                                                                                                                    
                                                                                                                                                    
  def _create_inventory(self, service, ctx):                                                                                                        
      resp = service.create_inventory(                                                                                                              
          goods_id=ctx["goods_id"],                                                                                                                 
          goods_code=ctx["goods_code"],                                                                                                             
      )                                                                                                                                             
                                                                                                                                                    
      inventory_id = extract(resp, "data.inventoryId")                                                                                              
      assert inventory_id, "inventory_id 不应为空"                                                                                                  
                                                                                                                                                    
      ctx["inventory_id"] = inventory_id                                                                                                            
                                                                                                                                                    
                                                                                                                                                    
  def _create_marketing(self, service, ctx):                                                                                                        
      resp = service.create_marketing(                                                                                                              
          goods_id=ctx["goods_id"],                                                                                                                 
          goods_code=ctx["goods_code"],                                                                                                             
          inventory_id=ctx["inventory_id"],                                                                                                         
      )                                                                                                                                             
                                                                                                                                                    
      activity_id = extract(resp, "data.activityId")                                                                                                
      assert activity_id, "activity_id 不应为空"
                                                                                                                                                    
      ctx["activity_id"] = activity_id                                                                                                              
                                                                                                                                                    
                                                                                                                                                    
  def _assert_marketing(self, service, ctx):                                                                                                        
      resp = service.query_marketing(                                                                                                               
          activity_id=ctx["activity_id"],                                                                                                           
      )                                                                                                                                             
                                                                                                                                                    
      Assertions.assert_eq(resp, "data.activityId", ctx["activity_id"])                                                                             
      Assertions.assert_eq(resp, "data.goodsCode", ctx["goods_code"])                                                                               
      Assertions.assert_eq(resp, "data.inventoryId", ctx["inventory_id"])                                                                           
                                                                                                                                                    
                                                                                                                                                    
  def _cleanup(self, service, ctx):                                                                                                                 
      if ctx.get("activity_id"):                                                                                                                    
          service.delete_marketing(activity_id=ctx["activity_id"])                                                                                  
                                                                                                                                                    
      if ctx.get("inventory_id"):                                                                                                                   
          service.delete_inventory(inventory_id=ctx["inventory_id"])                                                                                
                                                                                                                                                    
      if ctx.get("goods_id"):                                                                                                                       
          service.delete_goods(goods_id=ctx["goods_id"])                                                                                            
                                                                                                                                                    
  ---------                                                                                                                                               
                                                                                                                                                    
  关键区别                                                                                                                                          
                                                                                                                                                    
  不用 scene_context 时,数据流是显式返回和传参:                                                                                                   
                                                                                                                                                    
  goods_id, goods_code = self._create_goods(service)                                                                                                
                                                                                                                                                    
  inventory_id = self._create_inventory(                                                                                                            
      service,                                                                                                                                      
      goods_id,                                                                                                                                     
      goods_code,                                                                                                                                   
  )                                                                                                                                                 
                                                                                                                                                    
  activity_id = self._create_marketing(                                                                                                             
      service,                                                                                                                                      
      goods_id,                                                                                                                                     
      goods_code,                                                                                                                                   
      inventory_id,                                                                                                                                 
  )                                                                                                                                                 
                                                                                                                                                    
  用 scene_context 时,数据流是统一放进上下文:                                                                                                     
                                                                                                                                                    
  self._create_goods(service, ctx)                                                                                                                  
  self._create_inventory(service, ctx)                                                                                                              
  self._create_marketing(service, ctx)                                                                                                              
                                                                                                                                                    
  每个步骤自己从 ctx 里拿需要的数据,也把自己产生的数据放回 ctx。                                                                                   
                                                                                                                                                    
  ---------                                                                                                                                               
                                                                                                                                                    
  可以类比成一个表                                                                                                                                  
                                                                                                                                                    
  执行到不同阶段时,scene_context 的内容不断增加:                                                                                                  
                                                                                                                                                    
  刚开始:                                                                                                                                          
  {}                                                                                                                                                
                                                                                                                                                    
  _create_goods 后:                                                                                                                                
  {                                                                                                                                                 
    "goods_id": "10001",                                                                                                                            
    "goods_code": "G202609010001"                                                                                                                   
  }                                                                                                                                                 
                                                                                                                                                    
  _create_inventory 后:                                                                                                                            
  {                                                                                                                                                 
    "goods_id": "10001",                                                                                                                            
    "goods_code": "G202609010001",                                                                                                                  
    "inventory_id": "INV90001"                                                                                                                      
  }                                                                                                                                                 
                                                                                                                                                    
  _create_marketing 后:                                                                                                                            
  {                                                                                                                                                 
    "goods_id": "10001",                                                                                                                            
    "goods_code": "G202609010001",                                                                                                                  
    "inventory_id": "INV90001",                                                                                                                     
    "activity_id": "ACT80001"                                                                                                                       
  }                                                                                                                                                 
                                                                                                                                                    
  所以它是一个"场景状态容器"。                                                                                                                      
                                                                                                                                                    
  ---------                                                                                                                                               
                                                                                                                                                    
  什么时候它比局部变量更有价值                                                                                                                      
                                                                                                                                                    
  我觉得至少满足下面两个条件,才值得用:                                                                                                            
                                                                                                                                                    
  1. 用例被拆成多个 helper 步骤函数                                                                                                                 
  2. 多个步骤之间共享的数据比较多                                                                                                                   
                                                                                                                                                    
  例如:                                                                                                                                            
                                                                                                                                                    
  def test_goods_full_flow(self, 业务系统A_inventory_service, scene_context):                                                                            
      self._create_goods(业务系统A_inventory_service, scene_context)                                                                                     
      self._create_sku(业务系统A_inventory_service, scene_context)                                                                                       
      self._create_inventory(业务系统A_inventory_service, scene_context)                                                                                 
      self._create_price(业务系统A_inventory_service, scene_context)                                                                                     
      self._create_marketing(业务系统A_inventory_service, scene_context)                                                                                 
      self._approve_marketing(业务系统A_inventory_service, scene_context)                                                                                
      self._assert_marketing(业务系统A_inventory_service, scene_context)                                                                                 
      self._cleanup(业务系统A_inventory_service, scene_context)                                                                                          
                                                                                                                                                    
  这时候如果不用 scene_context,参数传递会越来越长。                                                                                                
                                                                                                                                                    
  ---------                                                                                                                                               
                                                                                                                                                    
  什么时候它没有价值                                                                                                                                
                                                                                                                                                    
  像你现在看到的这条:                                                                                                                              
 
  query_resp = 业务系统A_inventory_service.list_inventory()                                                                                              
  new_goods_code = extract(query_resp, "data.list[0].goodsCode")                                                                                    
  scene_context["temp_goods_code"] = new_goods_code                                                                                                 
                                                                                                                                                    
  marketing_resp = 业务系统A_inventory_service.list_inventory(                                                                                           
      overrides={                                                                                                                                   
          "goodsCode": scene_context.get("temp_goods_code"),                                                                                        
      }                                                                                                                                             
  )                                                                                                                                                 
                                                                                                                                                    
  它没有拆 helper,也没有大量中间状态,只是"存一下再取一下"。                                                                                       
                                                                                                                                                    
  这种场景用局部变量就够了:                                                                                                                        
                                                                                                                                                    
  query_resp = 业务系统A_inventory_service.list_inventory()                                                                                              
  goods_code = extract(query_resp, "data.list[0].goodsCode")                                                                                        
                                                                                                                                                    
  marketing_resp = 业务系统A_inventory_service.list_inventory(                                                                                           
      overrides={"goodsCode": goods_code}                                                                                                           
  )                                                                                                                                                 
                                                                                                                                                    

2)shared_context/ fixture

适合跨用例共享。

python 复制代码
fixture 就是这个意思:只要你把它写进测试函数参数里,pytest 会自动先执行这个 fixture 函数,再把返回值注入给测试用例。                                                    
                                                                                                                                                    
 1. fixture 会不会自动调用?                                                                                                                       
                                                                                                                                                    
  会。                                                                                                                                              
                                                                                                                                                    
  比如:                                                                                                                                            
                                                                                                                                                    
  @pytest.fixture                                                                                                                                   
  def inventory_goods(业务系统A_inventory_service):                                                                                                      
      resp = 业务系统A_inventory_service.list_inventory()                                                                                                
      goods_code = extract(resp, "data.list[0].goodsCode")                                                                                          
      goods_id = extract(resp, "data.list[0].goodsId")
                                                                                                                                                    
      assert goods_code, "goods_code 不应为空"                                                                                                      
      assert goods_id, "goods_id 不应为空"                                                                                                          
                                                                                                                                                    
      return {                                                                                                                                      
          "goods_code": goods_code,                                                                                                                 
          "goods_id": goods_id,                                                                                                                     
      }                                                                                                                                             
                                                                                                                                                    
  测试里这样写:                                                                                                                                    
                                                                                                                                                    
  def test_query_with_inventory_goods(self, 业务系统A_inventory_service, inventory_goods):                                                               
      ...                                                                                                                                           
                                                                                                                                                    
  pytest 会自动做这几步:                                                                                                                           
                                                                                                                                                    
  1. 发现测试函数需要 inventory_goods                                                                                                               
  2. 先执行 inventory_goods() 这个 fixture                                                                                                          
  3. 把返回的字典传给测试函数                                                                                                                       
  4. 测试函数里直接用这个数据                                                                                                                       
                                                                                                                                                    
  所以 fixture 的作用确实就是:                                                                                                                     
  把"准备数据"从测试用例本身剥离出去,让每个用例自己拿自己的数据。                                                                                  
                                                                                                                                                    
  ---------                                                                                                                                               
                                                                                                                                                    
  2. 这样为什么能解除用例依赖?                                                                                                                     
                                                                                                                                                    
  因为数据不是"上一个 test 存给下一个 test",而是"每个 test 自己通过 fixture 拿到"。                                                                
                                                                                                                                                    
  而项目里的的 shared_context 是这种模式:                                                                                                              
                                                                                                                                                    
  test_store_goods_code 负责写入                                                                                                                    
  test_query_with_stored_goods 负责读取                                                                                                             
                                                                                                                                                    
  这就有顺序依赖,会导致用例无法独立运行。                                                                                                                                  
                                                                                                                                                    
  而 fixture 是这种模式:                                                                                                                           
                                                                                                                                                    
  test_a -> 自己调用 fixture 拿数据                                                                                                                 
  test_b -> 自己调用 fixture 拿数据                                                                                                                 
                                                                                                                                                    
  两个用例互不依赖。                                                                                                                                
                                                                                                                                                    
  ---------                                                                                                                                               
                                                                                                                                                    
  3. fixture 和 shared_context 的区别                                                                                                               
                                                                                                                                                    
  可以这样理解:                                                                                                                                    

  - fixture:每个用例需要什么,就自己声明什么                                                                                                       
  - shared_context:先往一个公共容器里塞数据,后面的用例再来拿                                                                                      
                                                                                                                                                    
  所以 fixture 更像"显式依赖",shared_context 更像"隐式共享"。                                                                                      
                                                                                                                                                    
  对于测试来说,显式通常更好。                                                                                                                      

  ---------                                                                                                                                               
                                                                                                                                                    
  4. shared_context 什么时候比较适用?                                                                                                              
                                                                                                                                                    
  先说结论:                                                                                                                                        
  适用场景比较少,且一般不如 fixture 自然。                                                                                                         
                                                                                                                                                    
  它更适合这种情况:                                                                                                                                
                                                                                                                                                    
  - 你在同一个测试会话里,确实想让多个地方读写同一份可变数据                                                                                        
  - 而且这份数据不是某一个 test 的专属输入                                                                                                          
  - 它更像"运行时共享状态"或"流程编排状态"                                                                                                          
                                                                                                                                                    
  ---------                                                                                                                                               
                                                                                                                                                    
  5. 一个更实际的 shared_context 场景                                                                                                               
                                                                                                                                                    
  假设你有一组"强流程依赖"的测试,不是独立用例,而是人为拆成多个阶段跑:                                                                            
                                                                                                                                                    
  1. 创建临时租户                                                                                                                                   
  2. 创建临时商品                                                                                                                                   
  3. 给商品上架                                                                                                                                     
  4. 做营销配置                                                                                                                                     
  5. 最后统一清理                                                                                                                                   
                                                                                                                                                    
  这组流程里,你不想每一步都把参数层层传递,也不想每个步骤都重复查询一次前面的结果。                                                                
  这时可以用 shared_context 保存中间状态:                                                                                                          
                                                                                                                                                    
  def test_bootstrap_tenant(shared_context, tenant_service):                                                                                        
      tenant_id = tenant_service.create_tenant(...)                                                                                                 
      shared_context["tenant_id"] = tenant_id                                                                                                       
                                                                                                                                                    
  def test_create_goods(shared_context, goods_service):                                                                                             
      tenant_id = shared_context.get("tenant_id")                                                                                                   
      assert tenant_id, "tenant_id 不存在,请先执行 test_bootstrap_tenant"                                                                          
                                                                                                                                                    
      goods_id = goods_service.create_goods(tenant_id=tenant_id, ...)                                                                               
      shared_context["goods_id"] = goods_id                                                                                                         
                                                                                                                                                    
  def test_publish_goods(shared_context, goods_service):                                                                                            
      tenant_id = shared_context.get("tenant_id")                                                                                                   
      goods_id = shared_context.get("goods_id")                                                                                                     
                                                                                                                                                    
      assert tenant_id, "tenant_id 不存在"                                                                                                          
      assert goods_id, "goods_id 不存在"                                                                                                            
                                                                                                                                                    
      goods_service.publish_goods(tenant_id=tenant_id, goods_id=goods_id)                                                                           
                                                                                                                                                    
  这种写法下,shared_context 的作用是:                                                                                                             
                                                                                                                                                    
  - 保存同一会话中的中间状态                                                                                                                        
  - 让后续阶段能继续用前面生成的数据
  - 不用把参数一直往下传                                                                                                                            
                                                                                                                                                    
  但这类场景有个前提:                                                                                                                              
  你接受测试之间的顺序依赖。                                                                                                                        
                                                                                                                                                    
  这也是为什么我说它不太适合常规单元/接口测试。                                                                                                     
                                                                                                                                                    
  ---------                                                                                                                                               
                                                                                                                                                    
  6. 但实际上,这种场景很多时候也更适合 fixture                                                                                                     
                                                                                                                                                    
  你会发现上面那个"创建租户 -> 创建商品 -> 上架 -> 营销 -> 清理"的流程,                                                                            
  很多时候更适合写成:                                                                                                                              
                                                                                                                                                    
  - 一个 session 级 fixture 负责创建基础数据                                                                                                        
  - 各测试只消费 fixture 返回的数据                                                                                                                 
  - 清理也放在 fixture teardown 里                                                                                                                  
                                                                                                                                                    
  也就是说,很多 shared_context 场景,最终都能被更干净地替换成 fixture。                                                                            
                                                                                                                                                    
  所以可以这样理解:                                                                                                                                
                                                                                                                                                    
  - fixture:推荐的标准方式                                                                                                                         
  - shared_context:少数情况下,为了跨步骤、跨函数、跨用例共享运行时状态,临时用一下                                                                
                                                                                                                                                    
  ---------                                                                                                                                               
                                                                                                                                                    
  7.  一句话总结                                                                                                                                     
                                                                                                                                                    
  - fixture:pytest 会自动调用,用来给每个用例独立准备数据,适合解除用例依赖                                                                        
  - shared_context:适合少数"必须共享同一份可变状态"的编排型场景,但不适合作为常规测试用例之间的传值方式    

fixture的4种scope:

python 复制代码
function (默认作用域):每个测试函数/方法执行前都会调用一次 fixture,执行完后立即销毁。隔离性最好。
import pytest

@pytest.fixture  # 默认就是 scope="function"
def cart():
    print("\n[Setup] 创建新购物车")
    return {"items": []}

def test_add_apple(cart):
    cart["items"].append("apple")
    assert "apple" in cart["items"]

def test_add_banana(cart):
    cart["items"].append("banana")
    assert "banana" in cart["items"]
    
class(类作用域):在同一个测试类中,fixture 只初始化一次,类里的所有测试方法共享同一个实例;换一个测试类则会重新初始化。    
@pytest.fixture(scope="class")
def browser():
    print("\n[Setup] 启动浏览器")
    driver = {"status": "opened"}
    yield driver
    print("\n[Teardown] 关闭浏览器")

class TestLogin:
    def test_open_login_page(self, browser):
        print("测试打开登录页")
        assert browser["status"] == "opened"

    def test_input_credentials(self, browser):
        print("测试输入账号密码")
        assert browser["status"] == "opened"

class TestDashboard:
    def test_view_profile(self, browser):
        # 这个测试属于另一个类,会重新触发 browser fixture 的 setup
        print("测试查看个人资料")  
        
module(模块作用域):在同一个测试文件(.py 模块)中,无论有多少测试函数或测试类,fixture 只初始化一次。
# test_order.py
@pytest.fixture(scope="module")
def db_connection():
    print("\n[Setup] 建立模块级数据库连接")
    conn = {"connected": True}
    yield conn
    print("\n[Teardown] 断开模块级数据库连接")

def test_insert_order(db_connection):
    assert db_connection["connected"]

class TestOrderQuery:
    def test_query_by_id(self, db_connection):
        assert db_connection["connected"]
    
    def test_query_by_date(self, db_connection):
        assert db_connection["connected"]      
        
session(会话作用域):整个 pytest 运行期间(所有测试文件和所有用例)只初始化一次,所有测试共享。
# conftest.py 或任意被识别的测试文件
@pytest.fixture(scope="session")
def auth_token():
    print("\n[Setup] 登录获取全局 token")
    token = "fake_jwt_token_12345"
    return token

def test_api_1(auth_token):
    print(f"用例1 使用 token: {auth_token}")

def test_api_2(auth_token):
    print(f"用例2 使用 token: {auth_token}")       

3)VariableStore

适合跨会话、跨运行持久化。

python 复制代码
 - 写入、读取、清理分开,符合真实项目习惯                                                                                                          
                                                                                                                                                    
  # testcases/业务系统A/test_e2e_goods_flow.py                                                                                                           
  import allure                                                                                                                                     
  import pytest                                                                                                                                     
                                                                                                                                                    
  from common.assertion import Assertions, extract                                                                                                  
  from common.variable_store import VariableStore
                                                                                                                                                    
                                                                                                                                                    
  @allure.epic("Inventory")                                                                                                                         
  @allure.feature("Goods E2E Flow")                                                                                                                 
  @pytest.mark.e2e                                                                                                                                  
  class Test业务系统AGoodsE2EFlow:         
      """类变量定义 key = 给字符串起个名字,然后用名字引用它。存入json的格式是 {
      "业务系统A_e2e_goods_code": "ABC001"} ,用例使用时是用self.STORE_KEY_CODE,本质上就是 Python 里"不要到处复制粘贴同一个字符串"这个基本原则的体现 """                                                                                                         
      STORE_KEY_CODE = "业务系统A_e2e_goods_code"                                                                                                        
      STORE_KEY_ID = "业务系统A_e2e_goods_id"                                                                                                            
                                                                                                                                                    
      @allure.story("VariableStore-步骤1:保存接口返回的商品标识")                                                                                  
      @pytest.mark.P2                                                                                                                               
      @pytest.mark.goods_flow                                                                                                                       
      def test_save_goods_to_variable_store(self, 业务系统A_inventory_service):                                                                          
          """                                                                                                                                       
          场景说明:                                                                                                                                
          - 首次执行时,从接口中取出 goodsCode / goodsId                                                                                            
          - 写入 VariableStore,供后续独立 pytest 运行复用                                                                                          
          - 适合保存创建结果、主键、token、临时环境标识等跨会话数据                                                                                 
          """                                                                                                                                       
          response = 业务系统A_inventory_service.list_inventory()                                                                                        

          goods_code = extract(response, "data.list[0].goodsCode")                                                                                  
          goods_id = extract(response, "data.list[0].goodsId")                                                                                      
                                                                                                                                                    
          assert goods_code, "goodsCode 不应为空"                                                                                                   
          assert goods_id, "goodsId 不应为空"                                                                                                       
                                                                                                                                                    
          store = VariableStore()                                                                                                                   
          store.update({                                                                                                                            
              self.STORE_KEY_CODE: goods_code,                                                                                                      
              self.STORE_KEY_ID: goods_id,
          })   
          #假设这句这么写的话,后面引用字段"业务系统A_e2e_goods_code"就很容易拼错,且拼错了也没有提示,而且如果哪天想要
          #把这字段改成"bizA_e2e_goods_code",那就是改很多处地方
          #store.update({
        #"业务系统A_e2e_goods_code": "G001",
        #"业务系统A_e2e_goods_id": "1001",
    #})                                                                                                                              
                                                                                                                                                    
          assert store.get(self.STORE_KEY_CODE) == goods_code                                                                                       
          assert store.get(self.STORE_KEY_ID) == goods_id                                                                                           
                                                                                                                                                    
      @allure.story("VariableStore-步骤2:读取上次保存的数据继续查询")                                                                              
      @pytest.mark.P2                                                                                                                               
      @pytest.mark.goods_flow                                                                                                                       
      def test_query_with_persisted_goods(self, 业务系统A_inventory_service):                                                                            
          """
          场景说明:                                                                                                                                
          - 该用例可以单独执行                                                                                                                      
          - 只要 runtime/variables.json 中存在上次保存的数据,就能继续使用                                                                          
          - 演示跨会话持久化,而不是同一次执行内的步骤串联                                                                                          
          """                                                                                                                                       
          store = VariableStore()
          goods_code = store.get(self.STORE_KEY_CODE)                                                                                               
          goods_id = store.get(self.STORE_KEY_ID)                                                                                                   
                                                                                                                                                    
          assert goods_code, (                                                                                                                      
              f"未找到 {self.STORE_KEY_CODE},请先执行 test_save_goods_to_variable_store"
          )                                                                                                                                         
          assert goods_id, (                                                                                                                        
              f"未找到 {self.STORE_KEY_ID},请先执行 test_save_goods_to_variable_store"                                                             
          )                                                                                                                                         

          response = 业务系统A_inventory_service.list_inventory(                                                                                         
              page_num=1,                                                                                                                           
              overrides={                                                                                                                           
                  "goodsCode": goods_code,                                                                                                          
                  "goodsId": goods_id,                                                                                                              
              },
          )                                                                                                                                         
                                                                                                                                                    
          Assertions.assert_eq(response, "data.list[0].goodsCode", goods_code)                                                                      
          Assertions.assert_eq(response, "data.list[0].goodsId", goods_id)
                                                                                                                                                    
      @allure.story("VariableStore-清理持久化数据")                                                                                                 
      @pytest.mark.P3                                                                                                                               
      @pytest.mark.goods_flow                                                                                                                       
      def test_clear_persisted_goods(self):
          """                                                                                                                                       
          场景说明:                                                                                                                                
          - 可选清理步骤                                                                                                                            
          - 在演示或联调结束后删除持久化变量,避免旧数据影响下一次执行                                                                              
          """
          store = VariableStore()                                                                                                                   
          store.delete(self.STORE_KEY_CODE)                                                                                                         
          store.delete(self.STORE_KEY_ID)                                                                                                                                                                                                                                                                                                                                                                        
                                                                                                                                                    
  适合 VariableStore 的真实场景一般是:
                                                                                                                                                    
  1. 创建资源后保留 ID,后续单独回归时继续用                                                                                                        
  2. 保存登录 token / session 标识,给后续独立用例复用                                                                                              
  3. 保存临时环境里生成的业务主键,供下一轮测试读取                                                                                                 
  4. 演示"上次跑完,这次还能拿到数据"的跨会话能力                                                                                                   

  不太适合的场景:                                                                                                                                  
                                                                                                                                                    
  - 同一个测试类里前后步骤串联                                                                                                                      
  - 强依赖执行顺序的流程链路                                                                                                                        
  - 并行跑且多个用例共用同一 key 的场景                                                                                                             
     

这三种方式其实是按粒度分层的:

  • 临时场景级:scene_context/局部变量

  • 会话级:shared_context/fixture

  • 运行持久化:VariableStore

这样设计的好处是层次非常清楚,不会把所有数据都塞进同一种容器里。

十一、为什么"查列表 -> 存变量 -> 再查询"是很好的 E2E 模式

例如:

python 复制代码
response = 业务系统A_inventory_service.list_inventory()
new_goods_code = extract(response, "data.list[0].goodsCode")
scene_context["temp_goods_code"] = new_goods_code

再在后续步骤中使用这个值:

python 复制代码
marketing_resp = 业务系统A_inventory_service.list_inventory(
    overrides={"goodsCode": scene_context.get("temp_goods_code")}
)

这种方式的好处是:

  1. 数据来自系统真实返回,不是人工猜的

  2. 用例之间有自然的业务联系

  3. 更适合验证完整链路

这就是典型的 E2E 思路。

十二、为什么数据库断言要和场景测试结合

你在某些用例里已经预留了数据库校验思路。

这一步非常重要,因为很多业务系统的真实问题,不是在接口返回,而是在最终数据落库和状态流转上。

例如:

  • 接口返回成功了,但数据库没更新

  • 接口返回了列表,但字段值与数据库不一致

  • 接口查到了结果,但业务状态其实不正确

所以如果接口自动化只停留在响应层,覆盖面还是不够。

数据库验证能把"看起来成功"变成"真正成功"。

十三、小结

这一篇讲的是"测试怎么更像业务"。

真正高质量的接口自动化,不应该只是一些静态断言,而应该具备这些能力:

  • 基于真实数据反向验证

  • 支持参数化和数据驱动

  • 支持多角色查询对比

  • 支持轻量并发稳定性验证

  • 支持跨步骤、跨用例、跨会话的数据传递

  • 必要时结合数据库做最终确认

相关推荐
海兰21 分钟前
【 Python 量化交易】第10章:八大经典策略
android·python·kotlin
IT码农-爱吃辣条35 分钟前
Milvus 向量数据库 Python 实战教程
数据库·python·milvus
这个人懒得名字都没写38 分钟前
OpenCV 官方教程中的测试图片Can’t find required data file
python·opencv
智驭未来掌门人44 分钟前
Python HTTP CRUD 完全指南:从入门到实战
python
烂蜻蜓1 小时前
Flask入门教程(二十四):请求对象API——获取客户端数据的完整指南
后端·python·flask
开开心心就好1 小时前
手机悬屏翻译工具外语游戏漫画APP全覆盖
android·前端·javascript·python·游戏·pdf·html
梦在远山后1 小时前
Python 中两种 Queue 的区别
python·langchain
狮子雨恋1 小时前
anaconda python环境和QT python环境不一致如何解决
开发语言·python·qt
旖旎夜光1 小时前
【AI入门】大模型介绍全解析:从模型、LLM 到提示词与嵌入
人工智能·笔记·python·学习·ai编程