代码之家  ›  专栏  ›  技术社区  ›  Mad Physicist

使用多个接受参数[duplicate]的fixture参数化测试

  •  2
  • Mad Physicist  · 技术社区  · 6 年前

    我正在测试我写的一个数学函数。我想提供数据,它从一个不同的装置数。问题是所有的夹具都接受自己的不同夹具参数。

    我运行的测试总是一样的( test_myfunc 在本例中),以及我要插入其中的fixture都具有相同的兼容返回值( clean_data noisy_data 在代码中)。因此,我想将这两个fixture“链接”在一起,以便其中一个为测试提供输入。

    下面是设置的样子:

    import numpy as np
    import pytest
    from scipy import stats
    
    def myfunc(x, y):
        return True
    
    _noises = {
        'normal': lambda scale, n: np.random.normal(scale=scale, size=n),
        'uniform': lambda scale, n: np.random.uniform(-scale, scale, size=n),
        'triangle': lambda scale, n: np.random.triangular(-scale, 0, scale, size=n),
    }
    
    @pytest.fixture(params=[10**x for x in range(1, 4)])
    def x_data(request):
        """ Run the test on a few different densities """
        return np.linspace(-10, 10, request.param)
    
    @pytest.fixture(params=[0, 1, 0xABCD, 0x1234])
    def random_seed(request):
        """ Run the test for a bunch of datasets, but reporoducibly """
        np.random.seed(request.param)
    
    @pytest.fixture(params=np.arange(0.5, 5.5, 0.5))
    def shape(request):
        """ Run the test with a bunch of different curve shapes """
        return request.param
    
    @pytest.fixture()
    def clean_data(x_data, shape):
        """ Get a datset with no noise """
        return shape, stats.gamma.pdf(x_data, shape)
    
    @pytest.fixture(params=["triangle", "uniform", "normal"])
    def noisy_data(request, clean_data, random_seed):
        shape, base = clean_data
        noise = _noises[request.param](10, base.shape)
        return shape, base + noise
    
    def test_myfunc(x_data, data):
        shape, y_data = data
        assert myfunc(x_data, y_data)
    

    自从 清除\u数据 噪声数据 fixtures返回相同类型的结果,我希望能够在我的测试中使用它们,一个接一个。如何使用多个接受参数的fixture运行单个测试?

    如果可能,我希望避免生成测试。我熟悉间接参数化测试的想法,例如 Running the same test on two different fixtures

    @pytest.fixture()
    def data(request):
        """ Get the appropriate datset based on the request """
        return request.getfuncargvalue(request.param)
    
    @pytest.mark.parametrize('data', ['clean_data', 'noisy_data'], indirect=True)
    def test_myfunc(x_data, data):
        shape, y_data = data
        assert myfunc(x_data, y_data)
    

    当我用

    pytest -v pytest-parametrized.py
    

    我得到了一系列错误,这些错误似乎都指向这样一个事实:间接夹具需要参数,但没有提供参数:

    _________________ ERROR at setup of test_myfunc[10-clean_data] _________________
    
    self = <_pytest.python.CallSpec2 object at 0x7f8a4ff06518>, name = 'shape'
    
        def getparam(self, name):
            try:
    >           return self.params[name]
    E           KeyError: 'shape'
    
    /usr/lib/python3.6/site-packages/_pytest/python.py:684: KeyError
    
    During handling of the above exception, another exception occurred:
    
    self = <SubRequest 'clean_data' for <Function 'test_myfunc[10-clean_data]'>>
    fixturedef = <FixtureDef name='shape' scope='function' baseid='pytest-parametrized.py' >
    
        def _compute_fixture_value(self, fixturedef):
            """
                Creates a SubRequest based on "self" and calls the execute method of the given fixturedef object. This will
                force the FixtureDef object to throw away any previous results and compute a new fixture value, which
                will be stored into the FixtureDef object itself.
    
                :param FixtureDef fixturedef:
                """
            # prepare a subrequest object before calling fixture function
            # (latter managed by fixturedef)
            argname = fixturedef.argname
            funcitem = self._pyfuncitem
            scope = fixturedef.scope
            try:
    >           param = funcitem.callspec.getparam(argname)
    
    /usr/lib/python3.6/site-packages/_pytest/fixtures.py:484: 
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    
    self = <_pytest.python.CallSpec2 object at 0x7f8a4ff06518>, name = 'shape'
    
        def getparam(self, name):
            try:
                return self.params[name]
            except KeyError:
                if self._globalparam is NOTSET:
    >               raise ValueError(name)
    E               ValueError: shape
    
    /usr/lib/python3.6/site-packages/_pytest/python.py:687: ValueError
    
    During handling of the above exception, another exception occurred:
    
    request = <SubRequest 'data' for <Function 'test_myfunc[10-clean_data]'>>
    
        @pytest.fixture()
        def data(request):
            """ Get the appropriate datset based on the request """
    >       return request.getfuncargvalue(request.param)
    
    pytest-parametrized.py:55: 
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    /usr/lib/python3.6/site-packages/_pytest/fixtures.py:439: in getfuncargvalue
        return self.getfixturevalue(argname)
    /usr/lib/python3.6/site-packages/_pytest/fixtures.py:430: in getfixturevalue
        return self._get_active_fixturedef(argname).cached_result[0]
    /usr/lib/python3.6/site-packages/_pytest/fixtures.py:455: in _get_active_fixturedef
        self._compute_fixture_value(fixturedef)
    /usr/lib/python3.6/site-packages/_pytest/fixtures.py:526: in _compute_fixture_value
        fixturedef.execute(request=subrequest)
    /usr/lib/python3.6/site-packages/_pytest/fixtures.py:778: in execute
        fixturedef = request._get_active_fixturedef(argname)
    /usr/lib/python3.6/site-packages/_pytest/fixtures.py:455: in _get_active_fixturedef
        self._compute_fixture_value(fixturedef)
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    
    self = <SubRequest 'clean_data' for <Function 'test_myfunc[10-clean_data]'>>
    fixturedef = <FixtureDef name='shape' scope='function' baseid='pytest-parametrized.py' >
    
        def _compute_fixture_value(self, fixturedef):
            """
                Creates a SubRequest based on "self" and calls the execute method of the given fixturedef object. This will
                force the FixtureDef object to throw away any previous results and compute a new fixture value, which
                will be stored into the FixtureDef object itself.
    
                :param FixtureDef fixturedef:
                """
            # prepare a subrequest object before calling fixture function
            # (latter managed by fixturedef)
            argname = fixturedef.argname
            funcitem = self._pyfuncitem
            scope = fixturedef.scope
            try:
                param = funcitem.callspec.getparam(argname)
            except (AttributeError, ValueError):
                param = NOTSET
                param_index = 0
                if fixturedef.params is not None:
                    frame = inspect.stack()[3]
                    frameinfo = inspect.getframeinfo(frame[0])
                    source_path = frameinfo.filename
                    source_lineno = frameinfo.lineno
                    source_path = py.path.local(source_path)
                    if source_path.relto(funcitem.config.rootdir):
                        source_path = source_path.relto(funcitem.config.rootdir)
                    msg = (
                        "The requested fixture has no parameter defined for the "
                        "current test.\n\nRequested fixture '{0}' defined in:\n{1}"
                        "\n\nRequested here:\n{2}:{3}".format(
                            fixturedef.argname,
                            getlocation(fixturedef.func, funcitem.config.rootdir),
                            source_path,
                            source_lineno,
                        )
                    )
    >               fail(msg)
    E               Failed: The requested fixture has no parameter defined for the current test.
    E               
    E               Requested fixture 'shape' defined in:
    E               pytest-parametrized.py:27
    E               
    E               Requested here:
    E               /usr/lib/python3.6/site-packages/_pytest/fixtures.py:526
    
    /usr/lib/python3.6/site-packages/_pytest/fixtures.py:506: Failed
    

    1 回复  |  直到 6 年前
        1
  •  5
  •   hoefling    6 年前

    pytest . 详见第349期: Using fixtures in pytest.mark.parametrize

    @pytest.fixture
    def data(request, clean_data, noisy_data):
        type = request.param
        if type == 'clean':
            return clean_data
        elif type == 'noisy':
            return noisy_data
        else:
            raise ValueError('unknown type')
    
    @pytest.mark.parametrize('data', ['clean', 'noisy'], indirect=True)
    def test_myfunc(x_data, data):
        shape, y_data = data
        assert myfunc(x_data, y_data)