You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否将返回列表的pytest fixture用作测试参数列表?

问题描述

我有一段抽象后的测试代码如下:

import os
import pytest

class Files:
    def __init__(self):
        self.file1 = "file1"
        self.file2 = "file2"
        self.file3 = "file3"
        self.file4 = "file4"
        self.file5 = "file5"

class MyRunner:
    def __init__(self, files_obj):
        self.files = files_obj

def get_files_to_copy(files_obj: Files):
    return [files_obj.file1, files_obj.file2, files_obj.file3]

@pytest.fixture(scope="class")
def runner():
    """ long time to run... """
    files_obj = Files()
    _runner = MyRunner(files_obj)
    return _runner

@pytest.fixture(scope="class")
def must_exist_files_list(runner):
    return get_files_to_copy(runner.files)

@pytest.mark.parametrize("file", must_exist_files_list)
def test_must_exist_files(file):
    assert os.path.exists(file), f"{file} does not exist"

我的需求是:runner fixture初始化耗时较长,且被其他测试复用;需要借助runner的files属性获取文件列表,用于test_must_exist_files测试。我尝试创建返回列表的must_exist_files_list fixture作为该测试的参数列表,但运行时报错:

TypeError: 'function' object is not iterable

请问有没有类似的实现方式?最优解决方案是什么?

解决方案

方案1:参数化文件属性名(推荐)

不需要额外fixture,直接对文件属性名做参数化,在测试中通过runner fixture获取对应文件路径。既复用了耗时的runner实例,又能让每个文件作为独立测试用例运行:

import os
import pytest

class Files:
    def __init__(self):
        self.file1 = "file1"
        self.file2 = "file2"
        self.file3 = "file3"
        self.file4 = "file4"
        self.file5 = "file5"

class MyRunner:
    def __init__(self, files_obj):
        self.files = files_obj

@pytest.fixture(scope="class")
def runner():
    """ long time to run... """
    files_obj = Files()
    _runner = MyRunner(files_obj)
    return _runner

@pytest.mark.parametrize("file_attr", ["file1", "file2", "file3"])
def test_must_exist_files(runner, file_attr):
    file_path = getattr(runner.files, file_attr)
    assert os.path.exists(file_path), f"{file_path} does not exist"

方案2:用pytest_generate_tests动态生成参数

如果文件列表需要从get_files_to_copy动态获取,不想硬编码属性名,可使用pytest_generate_tests钩子在测试收集阶段生成参数。注意要和原runner fixture的初始化逻辑保持一致,避免重复耗时操作:

import os
import pytest

class Files:
    def __init__(self):
        self.file1 = "file1"
        self.file2 = "file2"
        self.file3 = "file3"
        self.file4 = "file4"
        self.file5 = "file5"

class MyRunner:
    def __init__(self, files_obj):
        self.files = files_obj

def get_files_to_copy(files_obj: Files):
    return [files_obj.file1, files_obj.file2, files_obj.file3]

@pytest.fixture(scope="class")
def runner():
    """ long time to run... """
    files_obj = Files()
    _runner = MyRunner(files_obj)
    return _runner

def pytest_generate_tests(metafunc):
    if "file" in metafunc.fixturenames:
        # 复用runner的初始化逻辑生成文件列表
        files_obj = Files()
        runner_inst = MyRunner(files_obj)
        files_list = get_files_to_copy(runner_inst.files)
        metafunc.parametrize("file", files_list)

def test_must_exist_files(runner, file):
    assert os.path.exists(file), f"{file} does not exist"

方案3:测试内遍历文件列表(简单但无独立用例)

如果不需要每个文件作为独立测试用例,可直接在测试中注入runner并遍历文件列表:

def test_must_exist_files(runner):
    files_list = get_files_to_copy(runner.files)
    for file in files_list:
        assert os.path.exists(file), f"{file} does not exist"

这种方式的缺点是:只要有一个文件不存在,测试会直接终止,无法看到剩余文件的检查结果。

内容的提问来源于stack exchange,提问作者noamgot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 10:02:14