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

如何在Pytest测试类的parametrize装饰器中使用self属性?

问题:在pytest类测试中使用对象属性实现parametrize

原本的函数式测试可以正常运行:

import pytest
from astropy.table import Table

@pytest.mark.parametrize('row', Table.read('testcases.fits'))
def test_cases(row):
    my_limit = 5.0
    assert abs(row['foo'] - row['bar']) < my_limit

但封装为类后,因为@pytest.mark.parametrize在类定义阶段就会执行,此时类实例尚未创建,self不存在,导致报错NameError: name 'self' is not defined:

import pytest
import matplotlib.pyplot as plt
from astropy.table import Table

class MyTest:
    def __init__(self):
        self.testcases = Table.read('testcases.fits')
        self.my_limit = 5.0

    @pytest.mark.parametrize('row', self.testcases)
    def test_cases(self, row):
        assert abs(row['foo'] - row['bar']) < self.my_limit

    def plot(self):
        plt.plot(self.testcases['foo'], self.testcases['bar'])

解决方案

方法一:将测试用例设为类属性

把testcases和my_limit定义为类属性,类定义阶段就能直接访问,无需依赖实例:

import pytest
import matplotlib.pyplot as plt
from astropy.table import Table

class MyTest:
    # 类属性,类定义时即可读取
    testcases = Table.read('testcases.fits')
    my_limit = 5.0

    @pytest.mark.parametrize('row', testcases)
    def test_cases(self, row):
        assert abs(row['foo'] - row['bar']) < self.my_limit

    def plot(self):
        plt.plot(self.testcases['foo'], self.testcases['bar'])

适用场景:测试用例固定,不需要动态生成的情况,代码最简洁。

方法二:使用间接参数化+Fixture

通过indirect=True让parametrize调用Fixture,间接获取实例属性:

import pytest
import matplotlib.pyplot as plt
from astropy.table import Table

class MyTest:
    def __init__(self):
        self.testcases = Table.read('testcases.fits')
        self.my_limit = 5.0

    @pytest.fixture
    def row_with_instance(self, request):
        # 返回测试用例行和当前类实例
        return request.param, self

    @pytest.mark.parametrize(
        'row_with_instance', 
        lambda self: self.testcases, 
        indirect=True
    )
    def test_cases(self, row_with_instance):
        row, self_instance = row_with_instance
        assert abs(row['foo'] - row['bar']) < self_instance.my_limit

    def plot(self):
        plt.plot(self.testcases['foo'], self.testcases['bar'])

适用场景:需要保留实例初始化逻辑(比如动态传入参数创建实例)的情况。

方法三:使用pytest_generate_tests钩子函数

在测试收集阶段动态生成参数,适合复杂场景:

import pytest
import matplotlib.pyplot as plt
from astropy.table import Table

class MyTest:
    def __init__(self):
        self.testcases = Table.read('testcases.fits')
        self.my_limit = 5.0

    def test_cases(self, row):
        assert abs(row['foo'] - row['bar']) < self.my_limit

    def plot(self):
        plt.plot(self.testcases['foo'], self.testcases['bar'])

# 钩子函数:为指定测试方法生成参数
def pytest_generate_tests(metafunc):
    # 判断当前测试是否属于MyTest类的test_cases方法
    if metafunc.cls is MyTest and metafunc.function.__name__ == "test_cases":
        # 创建实例并获取测试用例
        test_instance = MyTest()
        metafunc.parametrize('row', test_instance.testcases)

适用场景:需要根据外部条件(如命令行参数)动态调整测试用例的复杂场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 12:07:48