多条用例的 @pytest.mark.parametrize 没有顺序执行的问题

问题场景:
一个原始yaml数据文件,接口A读取初始数据,执行之后,将返回的部分关联数据回写到yaml文件,接口b载读取更新后的yaml数据文件,然后执行请求。

def getInterfaceCaceLists(interface_number,flag):
     # 根据interface_number 获取yaml内容并返回
      print("开始 %d 替换 %s" % (flag,interface_number))
      return interface_dict.get("case_lists")

@pytest.mark.parametrize("case_data", getInterfaceCaceLists("create_activity",1))
def test_create_activity(case_data):
      # url, headers 固定 ,请求数据参数化
      response = requests.post(url=url,headers=headers,data=case_data["params"])
      # 返回参数回写yaml
      writeback(response.text)


@pytest.mark.parametrize("case_data", getInterfaceCaceLists("create_father_config",2))
def test_create_father_config(case_data):
      # url, headers 固定 ,请求数据参数化
      response = requests.post(url=url,headers=headers,data=case_data["params"])
      # 返回参数回写yaml
      writeback(response.text)

期望的是:先执行test_create_activity ,获取数据,参数化执行,返回参数回写到yaml;然后执行test_create_father_config,获取更新后的数据,参数化执行。

实际执行中发现:在执行接口请求前,先执行了两次@pytest.mark.parametrize,分别获取参数化数据,再执行的接口请求。这就导致了 test_create_father_config 取到的是空数据,导致接口失败。

请问 如何让其按顺序执行呢? 或者 有什么替换解决方案呢 ?

1 Like

pytest-ordering 这个插件了解下

pytest-ordering 改变了用例的执行顺序。但是 还是先执行了每条用例的@pytest.mark.parametrize,然后取得参数化数据还是空的。
image

不晓得是控制了先执行所有的
@pytest.mark.parametrize

或者你用pytest的钩子函数 pytest_collection_modifyitems试下能不能满足你要求了

pytest_collection_modifyitems 在用例收集后可以改变用例顺序。但是不是用例顺序的问题。 是 pytest在用例收集阶段直接执行了装饰器@pytest.mark.parametrize的功能,收集到了空的数据。再实际执行用例时却没有再次收集数据。

期望的是 :在执行用例前才执行收集参数操作,从后正确的获取之前用例回写到yaml中的参数。

或者把需要用到yaml文件数据的测试用例。在page层就读取yaml文件里的数据?

pytest的执行流程就是先进行全部用例的参数化操作,根据参数化将用例分解为多个非参数化用例,然后再进行执行
所以你的数据准备步骤无法在用例中进行,pytest没有这种先将一个用例完全执行完毕再去进行下一个用例参数化的操作。。

1 Like

请教一下,你的问题解决了吗?我也遇到同样的问题,求指导 :grinning:

你好,请问问题解决了吗?我也是遇到一样的问题了 :hot_face:

同样的问题 :joy:,请问你们解决了吗?怎么解决的?

在收集阶段 pytest 必须知道要运行的所有测试,然后在继续。

不太优雅的实现,在case里面遍历

def getInterfaceCaceLists(a, b):
    def get_data():
        print("开始 %s 替换 %s" % (a, b))
        return [a, b]  # 模拟返回值

    return get_data


@pytest.mark.parametrize('case_data', [getInterfaceCaceLists("test_a", 1)])
def test_a(case_data):
    for case in case_data():
        print(f'case is {case}')  # 遍历


@pytest.mark.parametrize('case_data', [getInterfaceCaceLists("test_b", 2)])
def test_b(case_data):
    for case in case_data():
        print(f'case is {case}')  # 遍历

你好,请问问题解决了吗?我也是遇到一样的问题了

老师的表达能力太羡慕了,说了我想说的。但是我表达不出来这样的效果 :rofl:

使用fixture试一下呢?

这个试了,依旧还是在用例执行前做参数化的替换,还是不行。。

好久不看没想到这么多道友遇到同样的问题。 如下附上我的最终解决方案:

  1. 工具类

自定义一个装饰器,在执行用例方法前读取yaml数据,执行用例后回写yaml数据。

    @staticmethod
    def decorator_case(func)->None:
        ''''
        自定义装饰器,http请求前格式化参数,请求后回写变量到配置文件,断言结果
        '''
        pd = ProcessData()
        def wrapper(self,case_data):
            # 获取用例数据
            pd.adapt_case_data(case_data)  
            # 传给执行的用例方法 
            result = func(self,case_data)
            response,case_data,interface_code = result
            # 数据回写
            pd.update_output_field(response,interface_code,case_data["case_number"],case_data["output_field"])
            # 断言
            Assertion.resultAssert(response, case_data["assertion"])
        return wrapper
  1. 在用例文件中使用该装饰器
CASE_TEMPLATE = Template(
'''
    @pytest.mark.parametrize("case_data",ProcessData.get_case_lists("${interface_code}","${module_code}"), ids=ProcessData.get_ids("${interface_code}","${module_code}"))
    @ProcessData().decorator_case
    def test_${interface_code}(self,case_data):
        url = ProcessData().replace_url("${url}")
        response = self.request.${method}(url=url, cookies=self.cookies, headers="${headers}", case_data=case_data)
        return (response, case_data,"${interface_code}")
'''
        )

大佬牛皮!

我想问下怎么在用例执行前获取pytest.mark.parametrize的返回参数